From 215cd19108d97376284192c29790b42a0b0e618a Mon Sep 17 00:00:00 2001 From: Joost VandeVondele Date: Mon, 21 Oct 2019 08:05:14 +0200 Subject: [PATCH 001/454] 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 17644eed..6f908fd2 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -102,6 +102,13 @@ public: 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 002/454] 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 6e59bb54..c70fbf60 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here if (value < singularBeta) { extension = 1; - singularLMR++; - - if (value < singularBeta - std::min(4 * depth, 36)) - singularLMR++; + singularLMR = true; } // Multi-cut pruning @@ -1106,7 +1103,8 @@ moves_loop: // When in check, search starts from here 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 003/454] 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 | 17 ++++++++--------- 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, 30 insertions(+), 35 deletions(-) diff --git a/src/Makefile b/src/Makefile index 70246f56..679eb8d9 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 a0ad09f0..6bbd72a7 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)) - - 873 * !pos.count(Them) - - 6 * mg_value(score) / 8 - + mg_value(mobility[Them] - mobility[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 - 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 fc99ec26..ef7821e0 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 1825b6e2..84f3d977 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 a0a9a306..e6c901ea 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 c70fbf60..e0dcc4d1 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // Don't search moves with negative SEE values if ( (!inCheck || evasionPrunable) - && (!givesCheck || !(pos.blockers_for_king(~pos.side_to_move()) & from_sq(move))) + && !(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 c7d20788..a9378b4b 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 476f1d64..680cd3ad 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 cc4008b3..5197e9fb 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 004/454] 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 e0dcc4d1..086761a3 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 @@ moves_loop: // When in check, search starts from here 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_all_stats(pos, ss, bestMove, bestValue, beta, prevSq, + quietsSearched, quietCount, capturesSearched, captureCount, depth); - 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)); - - } // Bonus for prior countermove that caused the fail low else if ( (depth >= 3 || PvNode) && !priorCapture) @@ -1564,6 +1555,51 @@ moves_loop: // When in check, search starts from here } + // 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) { + + 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); + + // 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; + } + } + + // update_continuation_histories() updates histories of the move pairs formed // by moves at ply -1, -2, and -4 with current move. @@ -1575,32 +1611,9 @@ moves_loop: // When in check, search starts from here } - // update_capture_stats() updates move sorting heuristics when a new capture best move is found + // update_quiet_stats() updates move sorting heuristics - 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))); - - if (pos.capture_or_promotion(move)) - captureHistory[moved_piece][to_sq(move)][captured] << 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; - } - } - - - // update_quiet_stats() updates move sorting heuristics when a new quiet best move is found - - 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 @@ moves_loop: // When in check, search starts from here 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 005/454] 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 086761a3..b571cdf1 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 006/454] 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 6bbd72a7..c1640d94 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 ef7821e0..0b91582e 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 84f3d977..3ddf7030 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 007/454] 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 c1640d94..959ccd84 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 008/454] 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 8317f545..979410ae 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 959ccd84..ea54e271 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 655eb993..60d17ad2 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 5197e9fb..13c3bbf2 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 009/454] 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 2b1a5517..9301dcfa 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 477b1655..8d748eee 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 e10f8d5d..ca38a662 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 ea54e271..cefd9db4 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 e6c901ea..2ec2729c 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 010/454] 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 b571cdf1..4b90ab1c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -999,13 +999,6 @@ moves_loop: // When in check, search starts from here && (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 680cd3ad..6eb00d63 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 0517afc5..087ed382 100644 --- a/src/thread.h +++ b/src/thread.h @@ -60,7 +60,7 @@ public: 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 011/454] 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 60d17ad2..6469c43a 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 012/454] 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 6469c43a..60d17ad2 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 013/454] 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 4b90ab1c..be296443 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 014/454] 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 a9378b4b..c0453492 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 015/454] 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 be296443..f9579ae0 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 @@ moves_loop: // When in check, search starts from here && !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 016/454] 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 f9579ae0..b484dcbe 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1022,7 +1022,7 @@ moves_loop: // When in check, search starts from here 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 017/454] 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 cefd9db4..07bacf8d 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 018/454] 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 07bacf8d..dbd2d6d8 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 b484dcbe..e75db243 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -955,7 +955,45 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 019/454] 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 dbd2d6d8..2b7ab396 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 020/454] 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 e75db243..24bcb9ad 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 @@ moves_loop: // When in check, search starts from here { 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 087ed382..2b1f92b2 100644 --- a/src/thread.h +++ b/src/thread.h @@ -61,6 +61,7 @@ public: 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 021/454] 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 24bcb9ad..b54ff196 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -974,8 +974,7 @@ moves_loop: // When in check, search starts from here 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 022/454] 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 2b7ab396..7760f705 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 023/454] 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 b54ff196..d3f38aae 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1084,7 +1084,8 @@ moves_loop: // When in check, search starts from here && ( !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 024/454] 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 7760f705..eb5719bd 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 025/454] 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 eb5719bd..4c876820 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 026/454] 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 d3f38aae..e976274f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,6 +1049,12 @@ moves_loop: // When in check, search starts from here && 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 027/454] 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 4c876820..116d5d66 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 028/454] 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 8f30bee4..58f05e66 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 116d5d66..e42b4f38 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 99bf1a13..6f0bdd76 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 029/454] 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 e42b4f38..1988e20e 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 030/454] 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 3ddf7030..04222a2a 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 031/454] 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 a59ea425..e2b42e6d 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 032/454] 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 e976274f..26010108 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1033,8 +1033,7 @@ moves_loop: // When in check, search starts from here // 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 033/454] 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 26010108..e8ca5c58 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 2b1f92b2..a1545072 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 034/454] 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 e8ca5c58..a6c93b43 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 035/454] 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 a6c93b43..f39f1de1 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1478,9 +1478,7 @@ moves_loop: // When in check, search starts from here && !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 036/454] 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 f39f1de1..21c36f50 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1064,8 +1064,7 @@ moves_loop: // When in check, search starts from here 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 037/454] 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 1988e20e..20d1059e 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 038/454] 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 20d1059e..e7d30825 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 60d17ad2..c11dc5ba 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 039/454] 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 21c36f50..c856980b 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 040/454] 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 c856980b..55e04ec3 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 041/454] 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 55e04ec3..b0bcc57a 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 4729; + - 4926; // Reset statScore to zero if negative and most stats shows >= 0 if ( ss->statScore < 0 @@ -1159,10 +1159,10 @@ moves_loop: // When in check, search starts from here ss->statScore = 0; // Decrease/increase reduction by comparing opponent's stat score (~10 Elo) - if (ss->statScore >= -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 @@ moves_loop: // When in check, search starts from here 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 042/454] 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 10ffdeae..5b807ee6 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 043/454] 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 | 162 +++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 89 deletions(-) diff --git a/src/position.cpp b/src/position.cpp index 6336a5ed..9644e02c 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); - - // 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) - stmAttackers &= ~st->blockersForKing[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 + // there are pinners on their original square. + if (st->pinners[~stm] & occ) + stmAttackers &= ~st->blockersForKing[stm]; + 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); - - stm = ~stm; // Switch side to move - - // Negamax the balance with alpha = balance, beta = balance+1 and - // add nextVictim's value. - // - // (balance, balance+1) -> (-balance-1, -balance) - // - assert(balance < VALUE_ZERO); - - balance = -balance - 1 - PieceValue[MG][nextVictim]; - - // 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) + // the bitboard 'attackers' any X-ray attackers behind it. + if ((bb = stmAttackers & pieces(PAWN))) { - if (nextVictim == KING && (attackers & pieces(stm))) - stm = ~stm; - break; - } - assert(nextVictim != KING); - } - return us != stm; // We break the above loop when stm loses -} + if ((swap = PawnValueMg - swap) < res) + break; + occ ^= lsb(bb); + attackers |= attacks_bb(to, occ) & pieces(BISHOP, QUEEN); + } + + else if ((bb = stmAttackers & pieces(KNIGHT))) + { + if ((swap = KnightValueMg - swap) < res) + break; + + occ ^= lsb(bb); + } + + else if ((bb = stmAttackers & pieces(BISHOP))) + { + 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)); + } + + else // KING + // If we "capture" with the king but opponent still has attackers, + // reverse the result. + return (attackers & ~pieces(stm)) ? res ^ 1 : res; + } + + 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 044/454] 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 b0bcc57a..57316d85 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1003,7 +1003,11 @@ moves_loop: // When in check, search starts from here // 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 045/454] 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 e7d30825..27fcd477 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 046/454] 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 57316d85..25a7cf44 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 047/454] 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 979410ae..4638b41a 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 e882aa43..0ea5ac72 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 58f05e66..f906e731 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 9301dcfa..78614fa2 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 2afd3766..45d51504 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 8d748eee..440de1ea 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 ca38a662..276b942e 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 e29f8777..4642e448 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 27fcd477..3a4adef3 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 cccdd25d..077de70c 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 40081e8d..148bf248 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 11d4c687..0e130878 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 b472c3fd..9ab1d81c 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 6f908fd2..2a5bc603 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 ddd05e4e..b1385c2f 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 0b91582e..8f6edffb 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 aeba93ad..c2e7c3f1 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 e39f2afa..025f5b82 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 105c95d7..cdedc9b6 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 04222a2a..c3f7872f 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 4c041716..bd17618f 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 9644e02c..7c226dee 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 2ec2729c..783bb4a3 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 c11dc5ba..647bd864 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 25a7cf44..f357db5e 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 c77ca3ad..a900d094 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 c0453492..99f1834b 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 264f6e84..df3ca4fe 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 6eb00d63..f55bcb22 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 a1545072..4de30edb 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 f8cb466b..0ef5c981 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 484aaa65..0848be42 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 41befff0..9301dc94 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 d3cd094e..0b4a59de 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 d087cc38..98b054d3 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 13c3bbf2..902c2cfc 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 6f0bdd76..9c84ade3 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 31b63e2f..b845889b 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 23c0c480..26fcf302 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 048/454] 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 f357db5e..dfc0e5bf 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 049/454] 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 276b942e..2ed6ebc2 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 3a4adef3..7c7ce95c 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 7c226dee..53d9b64e 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 783bb4a3..6791455f 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 99f1834b..721a0ef5 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 050/454] 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 | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 45d51504..70114f20 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)); + 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 (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)); + } - 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]); + // 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); + }; - if (is_ok(to) && distance(s, to) < 3) - { - if (pt == PAWN) - PawnAttacks[c][s] |= to; - else - PseudoAttacks[pt][s] |= to; - } - } + 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 051/454] 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 dfc0e5bf..6146bdf6 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here && (*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 @@ moves_loop: // When in check, search starts from here + (*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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 052/454] 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 2a5bc603..053ee67e 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 b1385c2f..b11c5aa8 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 9c84ade3..8b35e6fd 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 053/454] 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 4638b41a..33a7a3d5 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 6146bdf6..ec9c6b1d 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 f55bcb22..615d482c 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 4de30edb..aea86fd5 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 054/454] 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 ec9c6b1d..7804119f 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 055/454] 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 33a7a3d5..a9f141f9 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 7804119f..035dd40a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1016,11 +1016,11 @@ moves_loop: // When in check, search starts from here // 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 056/454] 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 035dd40a..c1ed1f95 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1186,6 +1186,10 @@ moves_loop: // When in check, search starts from here 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 057/454] 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 c1ed1f95..0eea4127 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1115,7 +1115,7 @@ moves_loop: // When in check, search starts from here // 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 058/454] 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 5b807ee6..a759eff6 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 059/454] 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 053ee67e..95862ebb 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 060/454] 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 95862ebb..053ee67e 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 061/454] 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 53d9b64e..de9722ff 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 6791455f..e5071d53 100644 --- a/src/position.h +++ b/src/position.h @@ -174,8 +174,8 @@ private: // 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 062/454] 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 78614fa2..7bc4a65c 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 063/454] 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 440de1ea..d11b7e73 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 064/454] 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 7bc4a65c..ed6ed208 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 065/454] 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 053ee67e..484d0b21 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 066/454] 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 0eea4127..21df156c 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 067/454] 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 21df156c..3bd1a92d 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 @@ moves_loop: // When in check, search starts from here // 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 068/454] 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 7c7ce95c..fad0771d 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 069/454] 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 3bd1a92d..1490a266 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1028,7 +1028,11 @@ moves_loop: // When in check, search starts from here 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 070/454] 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 fad0771d..a1a3b4ed 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 071/454] 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 a1a3b4ed..be39f8a7 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 072/454] 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 484d0b21..0bae9f1e 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 b11c5aa8..45d9951a 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 0b4a59de..080d3a6b 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 98b054d3..142afd90 100644 --- a/src/tt.h +++ b/src/tt.h @@ -57,24 +57,22 @@ private: }; -/// 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 073/454] 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 be39f8a7..8d7976d5 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 074/454] 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 1490a266..044187e5 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 075/454] 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 044187e5..84f9bb23 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 076/454] 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 080d3a6b..46860fe9 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 077/454] 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 0bae9f1e..cf18d2e2 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 078/454] 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 8d7976d5..bf1eee9b 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 84f9bb23..985af7bc 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 079/454] 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 985af7bc..9562bf86 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1115,7 +1115,7 @@ moves_loop: // When in check, search starts from here // 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 080/454] 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 bf1eee9b..17597648 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 081/454] 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 17597648..df7ff5ea 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 082/454] 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 ed6ed208..bef2dc49 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 4642e448..f6135354 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 df7ff5ea..ceba2588 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 148bf248..182cf105 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 cf18d2e2..4d6483e7 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 45d9951a..a3780ba5 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 8f6edffb..7e8961ae 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 de9722ff..5efd9c42 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 615d482c..10ec96dd 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 aea86fd5..63629e33 100644 --- a/src/thread.h +++ b/src/thread.h @@ -56,7 +56,7 @@ public: 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 46860fe9..7e95a2a4 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 083/454] 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 5efd9c42..0ac45057 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 084/454] 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 9562bf86..c8a35a36 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1048,7 +1048,7 @@ moves_loop: // When in check, search starts from here && 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 085/454] 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 2ed6ebc2..6745ee26 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 c8a35a36..f1416a74 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } - // 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 902c2cfc..7ab7560a 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 086/454] 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 f1416a74..c3ebf9ab 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 087/454] 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 6745ee26..74e16fa6 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 088/454] 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 025f5b82..575c9022 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 cdedc9b6..33c4b086 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 @@ public: 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 @@ private: const Position& pos; const ButterflyHistory* mainHistory; + const LowPlyHistory* lowPlyHistory; const CapturePieceToHistory* captureHistory; const PieceToHistory** continuationHistory; Move ttMove; @@ -145,6 +154,7 @@ private: Square recaptureSquare; Value threshold; Depth depth; + int ply; ExtMove moves[MAX_MOVES]; }; diff --git a/src/search.cpp b/src/search.cpp index c3ebf9ab..3f860ac5 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 10ec96dd..b5cb87d9 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 63629e33..41d2b8f6 100644 --- a/src/thread.h +++ b/src/thread.h @@ -71,6 +71,7 @@ public: 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 089/454] 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 ceba2588..25aba644 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 090/454] 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 3f860ac5..a32ff4b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1339,7 +1339,7 @@ moves_loop: // When in check, search starts from here 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 091/454] 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 25aba644..06366e09 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 092/454] 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 a32ff4b6..544c3ee5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -957,7 +957,7 @@ moves_loop: // When in check, search starts from here 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 093/454] 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 679eb8d9..15ad6353 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 d11b7e73..ca161481 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 74e16fa6..5fdd307e 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 06366e09..31272f2c 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 077de70c..7c8a2a6f 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 647bd864..8bad7ed4 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 544c3ee5..7f6abf15 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 7ab7560a..58d05d2c 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 094/454] 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 5fdd307e..16c072be 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 8bad7ed4..f6f5933c 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 721a0ef5..6f369455 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 58d05d2c..d4937fd6 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 095/454] 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 16c072be..73a44633 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 096/454] 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 ca161481..b0e27233 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 097/454] 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 73a44633..748b05ff 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 098/454] 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 748b05ff..53c9ec83 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 099/454] 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 a9f141f9..7657acee 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 b0e27233..3ea18dd8 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 31272f2c..5d073b15 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 0e130878..7e212461 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 a3780ba5..e0e0e98b 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 c3f7872f..9981ac01 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 7f6abf15..3d130efc 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 @@ moves_loop: // When in check, search starts from here 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 100/454] 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 | 46 +++++++++++++++--------------------------- src/evaluate.cpp | 2 +- src/pawns.cpp | 2 +- src/psqt.cpp | 3 ++- src/syzygy/tbprobe.cpp | 2 +- src/types.h | 4 ---- 8 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index f906e731..f338cdda 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 3ea18dd8..f1d14603 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 53c9ec83..0a2b02ad 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 - }; + 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); + } - // 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 - }; + // 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 5d073b15..40630d22 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 9981ac01..560fd76b 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 f6f5933c..d86e98e4 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 6f369455..2532bbd3 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 d4937fd6..71893c0f 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 101/454] 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 7657acee..4826d1c4 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 3d130efc..f9910fb7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1175,13 +1175,6 @@ moves_loop: // When in check, search starts from here + (*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 102/454] 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 7e8961ae..9964ad34 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 103/454] 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 f9910fb7..70520ac9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1024,10 +1024,9 @@ moves_loop: // When in check, search starts from here 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 104/454] 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 f1d14603..529e3dfe 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 0a2b02ad..56583c58 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 0ac45057..fefce56e 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 70520ac9..a54e181a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1193,10 +1193,16 @@ moves_loop: // When in check, search starts from here 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 2532bbd3..34e4331d 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 142afd90..8b70f797 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 71893c0f..bfcd3f23 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 8b35e6fd..33577a4e 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 105/454] 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 40630d22..fad2a785 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 106/454] 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 a54e181a..fe57806c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1149,7 +1149,7 @@ moves_loop: // When in check, search starts from here // 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 107/454] 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 fe57806c..abc6874e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,9 +1055,9 @@ moves_loop: // When in check, search starts from here && 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 108/454] 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 56583c58..1a5959e5 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 f6135354..49ebb603 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 109/454] 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 fad2a785..d51325f0 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 110/454] 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 abc6874e..c5a7582c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1182,7 +1182,7 @@ moves_loop: // When in check, search starts from here 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 111/454] 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 c5a7582c..f866afe5 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 112/454] 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 560fd76b..3023021d 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 113/454] 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 4826d1c4..79eb98a0 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 f866afe5..993fa853 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -962,6 +962,7 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here && 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 @@ moves_loop: // When in check, search starts from here 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 114/454] Small cleanups https://github.com/official-stockfish/Stockfish/pull/2584 No functional change. --- src/bitboard.cpp | 34 ++++++++++++++-------------------- 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, 37 insertions(+), 43 deletions(-) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 70114f20..bb03dfeb 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 (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 }; - - init_magics(RookTable, RookMagics, RookDirections); - init_magics(BishopTable, BishopMagics, BishopDirections); - 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); + + for (int step : {-17, -15, -10, -6, 6, 10, 15, 17} ) + PseudoAttacks[KNIGHT][s1] |= landing_square_bb(s1, step); + 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 d51325f0..63541c2a 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 7e212461..93699f5f 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 9964ad34..804ef87b 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 575c9022..580c6d75 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 3023021d..8759631a 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 fefce56e..6bbb7914 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 993fa853..38d3204c 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 33577a4e..11d5adc6 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 115/454] 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 | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/position.h b/src/position.h index e5071d53..f79c5463 100644 --- a/src/position.h +++ b/src/position.h @@ -83,7 +83,6 @@ public: 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 bool Position::empty(Square s) const { + return piece_on(s) == NO_PIECE; +} + inline Piece Position::moved_piece(Move m) const { - return board[from_sq(m)]; + return piece_on(from_sq(m)); } -inline Bitboard Position::pieces() const { - return byTypeBB[ALL_PIECES]; -} - -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 116/454] 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 38d3204c..ad5b364d 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 117/454] 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 63541c2a..57491f34 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 118/454] 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 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/search.cpp b/src/search.cpp index ad5b364d..eb30d9fa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1189,10 +1189,17 @@ moves_loop: // When in check, search starts from here // Decrease/increase reduction for moves with a good/bad history (~30 Elo) r -= ss->statScore / 16434; } + else + { + // Increase reduction for captures/promotions if late move and at low depth + if (depth < 8 && moveCount > 2) + r++; - // Increase reduction for captures/promotions if late move and at low depth - else 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 119/454] 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 1a5959e5..e232da62 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 49ebb603..fd1aba2d 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 120/454] 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 57491f34..f4a5d486 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 121/454] 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 eb30d9fa..cfda94b8 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 122/454] 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 cfda94b8..dba8857e 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 @@ moves_loop: // When in check, search starts from here MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->lowPlyHistory, - &thisThread->captureHistory, + &captureHistory, contHist, countermove, ss->killers, @@ -1010,12 +1012,12 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 123/454] 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 bb03dfeb..69bbc77b 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; + { + Square s = sq; + while(safe_destination(s, directions[i]) && !(occupied & s)) + attacks |= (s += directions[i]); + } - if (occupied & s) - break; - } - - return attack; + return attacks; } diff --git a/src/bitboard.h b/src/bitboard.h index 529e3dfe..9252c3dc 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 f4a5d486..a2e5ef7b 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 804ef87b..a3abcde8 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 8759631a..0017b804 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 d86e98e4..7fa36ac8 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 dba8857e..4abc6069 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } 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 34e4331d..f1fd695c 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 b5cb87d9..88331f06 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 41d2b8f6..79be197b 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 bfcd3f23..cd8d2320 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 124/454] 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 580c6d75..b1e10587 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 125/454] 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 4abc6069..4073f21d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1110,6 +1110,12 @@ moves_loop: // When in check, search starts from here 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 126/454] 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 a2e5ef7b..2698c813 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 0017b804..63bc596f 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 127/454] 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 2698c813..cd535d88 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 63bc596f..7c4eda0f 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 bd17618f..41a88c6f 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 128/454] 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 4073f21d..76011840 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1398,7 +1398,7 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion, evasionPrunable; + bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1527,14 +1527,8 @@ moves_loop: // When in check, search starts from here } } - // 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 129/454] 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 7c4eda0f..a2063a8f 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 130/454] 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 cd535d88..8feedfeb 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 a2063a8f..75e6ad7a 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 131/454] 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 76011840..dae1e23c 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 132/454] 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 8feedfeb..7eafacf8 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 133/454] 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 7eafacf8..0b1956f1 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 134/454] 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 0b1956f1..cbcebd04 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 135/454] 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 dae1e23c..fd690dcd 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 136/454] 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 75e6ad7a..066146e2 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 137/454] 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 fd690dcd..a7e90a08 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1091,6 +1091,18 @@ moves_loop: // When in check, search starts from here // 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 138/454] 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 a7e90a08..a3d4a329 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // Update the current move (this must be done after singular extension search) ss->currentMove = move; - ss->continuationHistory = &thisThread->continuationHistory[inCheck] + ss->continuationHistory = &thisThread->continuationHistory[ss->inCheck] [captureOrPromotion] [movedPiece] [to_sq(move)]; @@ -1365,11 +1365,11 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, inCheck, givesCheck, captureOrPromotion; + bool ttHit, pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1426,20 +1426,20 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here return ttValue; // Evaluate the position statically - if (inCheck) + if (ss->inCheck) { ss->staticEval = VALUE_NONE; bestValue = futilityBase = -VALUE_INFINITE; @@ -1520,7 +1520,7 @@ moves_loop: // When in check, search starts from here moveCount++; // Futility pruning - if ( !inCheck + if ( !ss->inCheck && !givesCheck && futilityBase > -VALUE_KNOWN_WIN && !pos.advanced_pawn_push(move)) @@ -1543,7 +1543,7 @@ moves_loop: // When in check, search starts from here } // 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 @@ moves_loop: // When in check, search starts from here } 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) { for (int i : {1, 2, 4, 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 a900d094..1653ce92 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 139/454] 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 a3d4a329..55c520a4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,6 +1049,13 @@ moves_loop: // When in check, search starts from here && 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 140/454] 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 cbcebd04..9d7728c4 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 066146e2..7b266e77 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 41a88c6f..a3284a0f 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 6bbb7914..40ebb959 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 f79c5463..34a6abc3 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 55c520a4..7f29f771 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 @@ moves_loop: // When in check, search starts from here || 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 141/454] 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 79eb98a0..9fceebf7 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 9d7728c4..874faa6b 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 142/454] 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 874faa6b..67e05921 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 143/454] 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 15ad6353..0998a551 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 182cf105..6eeda66d 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 00000000..fe61151f --- /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 00000000..27c3f961 --- /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 cd8d2320..7b896803 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 144/454] 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 67e05921..e663f21f 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 145/454] 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 0998a551..016aafec 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 146/454] 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 4d6483e7..94681008 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 147/454] 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 7b896803..580c846a 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 148/454] 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 9fceebf7..36c2a47b 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 f1fd695c..843f049a 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 @@ public: 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 149/454] 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 a759eff6..35ff095d 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 6eeda66d..c7cf2c6f 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 94681008..b1c0feeb 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 e0e0e98b..9d53c2da 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 7e95a2a4..6ee63138 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 150/454] 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 e663f21f..d972db5a 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 151/454] 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 c7cf2c6f..6eeda66d 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 b1c0feeb..e0cc6ed5 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 9d53c2da..05bfc7de 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 6ee63138..4e06bed9 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 8b70f797..bd723a86 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 152/454] 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 | 111 +++++++++++++++++----------------------------- src/ucioption.cpp | 4 +- 2 files changed, 42 insertions(+), 73 deletions(-) diff --git a/src/timeman.cpp b/src/timeman.cpp index 0848be42..f794ab13 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) + // 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)); + + // 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) { - // Calculate thinking time for hypothetical "moves to go"-value - hypMyTime = limits.time[us] - + limits.inc[us] * (hypMTG - 1) - - moveOverhead * (2 + std::min(hypMTG, 40)); - - hypMyTime = std::max(hypMyTime, TimePoint(0)); - - TimePoint t1 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - TimePoint t2 = minThinkingTime + remaining(hypMyTime, hypMTG, ply, slowMover); - - optimumTime = std::min(t1, optimumTime); - maximumTime = std::min(t2, maximumTime); + 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); } + // 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 26fcf302..ad576fda 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 153/454] 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 843f049a..adc45d58 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 154/454] 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 f794ab13..0021e96b 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 ad576fda..66fd42d1 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 155/454] 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 0021e96b..45e9db58 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 156/454] 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 e0cc6ed5..c6254784 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 157/454] 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 | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index d972db5a..d04d724a 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])); + helpers = shift(pos.pieces(Us, PAWN)) + & ~pos.pieces(Them) + & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]); - // Remove candidate otherwise - b &= ~candidatePassers - | shift(leverable) - | shift(leverable); + // 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 158/454] 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 d04d724a..449cf6d7 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 b1e10587..e26f42ef 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -169,7 +169,7 @@ top: 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 159/454] 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 449cf6d7..e80e9442 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 160/454] 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 7b266e77..c20cb529 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 161/454] 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 e2b42e6d..e2ae61be 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 162/454] 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 7f29f771..5e9cd463 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 88331f06..2e0c216e 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 163/454] 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 69bbc77b..f650eef6 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 e80e9442..2d1f4b9e 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 6eeda66d..fafefee2 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 a3abcde8..5787d174 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 c20cb529..b883dda2 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 40ebb959..f5ff3da1 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 34a6abc3..ae624926 100644 --- a/src/position.h +++ b/src/position.h @@ -98,7 +98,7 @@ public: 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 5e9cd463..3b3c0f2a 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 adc45d58..6bfd78ad 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 2e0c216e..c1713122 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 45e9db58..1f598745 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 4e06bed9..0a3c54a1 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 fe61151f..696b4cb8 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 66fd42d1..16add76e 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 164/454] 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 2d1f4b9e..7aa67f26 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 165/454] 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 b883dda2..f9dbcae2 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 166/454] 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 f9dbcae2..8354cc15 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 167/454] 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 7aa67f26..8e8cc091 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 168/454] 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 8354cc15..3ce89630 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 169/454] 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 bef2dc49..be6f0d0a 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 9252c3dc..93f838f8 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 e232da62..7b9c145e 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 8e8cc091..ad79db5c 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 5787d174..81b8c929 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 3ce89630..467137b3 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 f5ff3da1..d2e33b30 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 ae624926..8f8c8f7a 100644 --- a/src/position.h +++ b/src/position.h @@ -112,9 +112,6 @@ public: // 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 170/454] 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 3b3c0f2a..4747beb2 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,6 +1055,7 @@ moves_loop: // When in check, search starts from here // 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 171/454] 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 467137b3..c1119a41 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 172/454] 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 ad79db5c..dc7134a8 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 173/454] 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 4747beb2..1e133447 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here if (value < singularBeta) { extension = 1; - singularLMR = true; + singularQuietLMR = !ttCapture; } // Multi-cut pruning @@ -1198,7 +1199,7 @@ moves_loop: // When in check, search starts from here 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 174/454] 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 1e133447..35110538 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 175/454] 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 81b8c929..b57f41a9 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 176/454] 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 e26f42ef..78102c52 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 35110538..efa7b9c5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -971,7 +971,7 @@ moves_loop: // When in check, search starts from here 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 177/454] 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 dc7134a8..12a4c7bf 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)); + mg += u; + eg += v; - return make_score(u, 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 178/454] 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 | 20 ++++++++++++++++---- src/evaluate.cpp | 2 +- src/movegen.cpp | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 93f838f8..704f4bb4 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)); } - -/// between_bb() returns squares that are linearly between the given squares +/// 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 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 12a4c7bf..c042c016 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 b57f41a9..17203a95 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 179/454] 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 efa7b9c5..e3a5a92e 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 c1713122..a27a60c6 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 79be197b..a69e1d10 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 180/454] 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 36c2a47b..1cd7ff54 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 f650eef6..3bb3ff8f 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 d2e33b30..c9db6224 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 181/454] 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 35ff095d..2b1de86b 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 182/454] 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 0a3c54a1..92aaee00 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 bd723a86..76db03da 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,6 +66,7 @@ private: class TranspositionTable { static constexpr int ClusterSize = 3; + static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -82,15 +83,21 @@ public: 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 16add76e..90190b53 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 183/454] 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 c042c016..b173cd3b 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 184/454] 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 e3a5a92e..f5887f3f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1006,7 +1006,8 @@ moves_loop: // When in check, search starts from here && 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 185/454] 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 b173cd3b..036b93a9 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 186/454] 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 f5887f3f..5ad650d2 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here && 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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here - 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 @@ moves_loop: // When in check, search starts from here 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 187/454] 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 5ad650d2..cf89a892 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here && 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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 188/454] 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 05bfc7de..373f1b77 100644 --- a/src/misc.h +++ b/src/misc.h @@ -110,6 +110,19 @@ public: { 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 cf89a892..67339ed7 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 92aaee00..d0a5d4e0 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 76db03da..3e1d0e99 100644 --- a/src/tt.h +++ b/src/tt.h @@ -66,7 +66,6 @@ private: class TranspositionTable { static constexpr int ClusterSize = 3; - static constexpr int ClustersPerSuperCluster = 256; struct Cluster { TTEntry entry[ClusterSize]; @@ -84,20 +83,13 @@ public: 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 90190b53..7037ea57 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 189/454] 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 7fa36ac8..abd23547 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 190/454] 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 67339ed7..d96ed7da 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1186,7 +1186,7 @@ moves_loop: // When in check, search starts from here // 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 191/454] 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 016aafec..41c2aff6 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 be6f0d0a..7e27eb96 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 3bb3ff8f..0bf7eef9 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 704f4bb4..0f55810c 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 036b93a9..3b0891a2 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 78102c52..5775f810 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 33c4b086..aaff388f 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 @@ public: 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 c1119a41..597dff2b 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 a3284a0f..e6098069 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 abd23547..27c7a36f 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 d96ed7da..563d1aab 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 6bfd78ad..95d58945 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 1f598745..d27962b7 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 580c846a..969d4e65 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 192/454] 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 | 384 ++++++++++++++++++++++++------------------------ 1 file changed, 194 insertions(+), 190 deletions(-) diff --git a/src/endgame.cpp b/src/endgame.cpp index 7b9c145e..d9e76348 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)); + 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 f = file_of(wpsq); - Rank r = rank_of(wpsq); - Square queeningSq = make_square(f, RANK_8); + 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 193/454] 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 3b0891a2..8b4a27bc 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 194/454] 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 563d1aab..671ac489 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1026,6 +1026,7 @@ moves_loop: // When in check, search starts from here 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 195/454] 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 41c2aff6..83e0bb14 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 196/454] 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 d27962b7..a61c36d7 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 7037ea57..871edb29 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 197/454] 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 1cd7ff54..f08d71d3 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 83e0bb14..81731e66 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)) + ifeq ($(comp),$(filter $(comp),gcc clang mingw)) CXXFLAGS += -flto LDFLAGS += $(CXXFLAGS) endif - - ifeq ($(comp),mingw) - ifeq ($(KERNEL),Linux) - 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 198/454] 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 0f55810c..1c598108 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 d9e76348..be0755a8 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 8b4a27bc..60ec9c72 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 93699f5f..bb25d3ca 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 9ab1d81c..21647f23 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 c6254784..2bc05c5b 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 597dff2b..d741b2ef 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 c9db6224..471ef01f 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 8f8c8f7a..8cfa3920 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 27c7a36f..c5da9785 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 671ac489..1e2980cb 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 @@ moves_loop: // When in check, search starts from here && !(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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } - // 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 @@ moves_loop: // When in check, search starts from here } // 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 a27a60c6..a0ee2b25 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 a61c36d7..546eadd2 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 d0a5d4e0..34590903 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 3e1d0e99..e18db8ce 100644 --- a/src/tt.h +++ b/src/tt.h @@ -60,8 +60,8 @@ private: /// 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 696b4cb8..c1b1c76b 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 969d4e65..0c512f5b 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 871edb29..c268c975 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 199/454] 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 1653ce92..3e855c8b 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 200/454] 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 81731e66..492403d3 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 201/454] 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 492403d3..c3660a20 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 202/454] 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 1e2980cb..0fa39988 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 0c512f5b..c1598561 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 203/454] 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 471ef01f..6ef7aedc 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 204/454] 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 c5da9785..5e8dd2c7 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 205/454] 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 60ec9c72..65f7bddc 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 206/454] 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 65f7bddc..d19cf34e 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 207/454] 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 d19cf34e..615df1ba 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 208/454] 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 2b1de86b..e60ac718 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 0fa39988..f14bdf77 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 11d5adc6..bb57c80b 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 b845889b..ad954d9f 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 c268c975..4befa6ac 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 209/454] 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 | 76 ++++++++++++++++++++++-------------------------- src/pawns.cpp | 4 ++- 2 files changed, 38 insertions(+), 42 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 615df1ba..48db2b3b 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; + kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)]; - // 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 + // 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 d741b2ef..d365ba12 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 210/454] 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 4befa6ac..ef54ef4e 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 211/454] 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 48db2b3b..bb1724a4 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 212/454] 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 be0755a8..40f49dce 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 213/454] 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 d365ba12..f18e0315 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 214/454] 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 6ef7aedc..396bff5f 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 215/454] 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 f18e0315..7f8d451a 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 216/454] 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 17203a95..4ff12fc6 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 f14bdf77..1610c206 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1486,8 +1486,8 @@ moves_loop: // When in check, search starts from here // 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 217/454] 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 e60ac718..823518d1 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 f338cdda..3299f373 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 1c598108..afeb40ec 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 bb1724a4..6f2dd69b 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 1610c206..720a9100 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 218/454] 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 720a9100..6cf2f90d 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 219/454] 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 afeb40ec..15ec4153 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 6f2dd69b..ca6ea5c4 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 220/454] 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 40f49dce..a8ceb648 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 221/454] 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 ca6ea5c4..dbb725d4 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 222/454] 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 6cf2f90d..17ccab92 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 223/454] 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 15ec4153..8c95de8c 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 dbb725d4..d16648a8 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 224/454] 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 17ccab92..6ec4d803 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1067,7 +1067,7 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 225/454] 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 d16648a8..b34d82f6 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 226/454] 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 6ec4d803..91ac60ad 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1167,6 +1167,13 @@ moves_loop: // When in check, search starts from here { 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 227/454] 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 | 129 ++++--- 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 | 114 +++++-- 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, 2474 insertions(+), 245 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 e2ae61be..d563a1e1 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 f08d71d3..2e080e61 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 823518d1..f71a8b34 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,21 +28,20 @@ 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 + +## UCI options 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. - - * #### 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. - * #### Threads The number of CPU threads used for searching a position. For best performance, set this equal to the number of CPU cores available. @@ -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,10 +56,32 @@ 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). + + * #### 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. + + * #### 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). * #### UCI_LimitStrength Enable weaker play aiming for an Elo rating as set by UCI_Elo. This option overrides Skill Level. @@ -66,28 +90,10 @@ 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. - - * #### 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_Chess960 - An option handled by your GUI. If true, Stockfish will play Chess960. - - * #### 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 21f3bbe3..d356ba2f 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 c3660a20..4741e722 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 3299f373..6041d642 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 7e27eb96..bbe8e9a7 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 0bf7eef9..f531010c 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 8c95de8c..a899d879 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 a8ceb648..c8be2198 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 fd1aba2d..1351d88a 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 b34d82f6..f43c62d6 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 @@ make_v: /// 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)"; - - std::memset(scores, 0, sizeof(scores)); - - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt - - Value v = Evaluation(pos).value(); - - v = pos.side_to_move() == WHITE ? v : -v; // Trace scores are from white's point of view + return "Final evaluation: none (in check)"; 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); + ss << std::showpoint << std::noshowpos << std::fixed << std::setprecision(2); + + Value v; + + 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 << "\nFinal evaluation: " << to_cp(v) << " (white side)\n"; diff --git a/src/evaluate.h b/src/evaluate.h index 7c8a2a6f..e808068d 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 fafefee2..fbad6622 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 bb25d3ca..0ef9926f 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 21647f23..80d01655 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 2bc05c5b..3d7c75e5 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 373f1b77..eb4e05c0 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 4ff12fc6..d74df4c3 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 c2e7c3f1..fb616d00 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 5775f810..96a44449 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 aaff388f..f080935a 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 00000000..9216bd41 --- /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 00000000..af0894b2 --- /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 00000000..5f0d1855 --- /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 00000000..79ca83ae --- /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 00000000..d00a35df --- /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 00000000..628add6e --- /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 00000000..99842eea --- /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 00000000..d9ad680a --- /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 00000000..b585bc87 --- /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 00000000..7ade598f --- /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 00000000..afca14c8 --- /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 00000000..2a354a3c --- /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 00000000..91cdc4bd --- /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 00000000..972ef3e5 --- /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 00000000..1cfebbe4 --- /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 7f8d451a..73682529 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 e6098069..5499826e 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 396bff5f..46e5d78b 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 8cfa3920..a77050eb 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 @@ public: 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 @@ private: 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 @@ private: 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 5e8dd2c7..eb36e75e 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 91ac60ad..d1dc4489 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 3e855c8b..2554f3fb 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 95d58945..20215b96 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 df3ca4fe..b998989b 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 a0ee2b25..44aea14e 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 a69e1d10..46da1e34 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 0ef5c981..c4b55a48 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 546eadd2..df4ba9b2 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 9301dc94..5ad72b32 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 34590903..d494c27d 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 e18db8ce..c177ca52 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 c1b1c76b..e94f67f8 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 27c3f961..1489fa32 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 c1598561..379859f7 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 bb57c80b..d6486320 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 ad954d9f..eb0b390b 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 ef54ef4e..788aed17 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 228/454] 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 f43c62d6..09496fdc 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 @@ make_v: 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 229/454] 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 09496fdc..015efa48 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 230/454] 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 73682529..868d0c8e 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 379859f7..73da41e2 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 231/454] 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 015efa48..d20c7b70 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 232/454] 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 d1dc4489..2f83f4f4 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -986,6 +986,10 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 233/454] 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 2e080e61..07e07297 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 788aed17..faeb78ae 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 234/454] 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 2f83f4f4..886ed52c 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 235/454] 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 d20c7b70..47b84ee6 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 236/454] 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 886ed52c..8be96e29 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,6 +1134,12 @@ moves_loop: // When in check, search starts from here // 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 237/454] 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 8be96e29..4a9bd7de 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1028,7 +1028,7 @@ moves_loop: // When in check, search starts from here 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 238/454] 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 47b84ee6..1ae6cb3a 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 4a9bd7de..4a993b01 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1134,7 +1134,7 @@ moves_loop: // When in check, search starts from here // 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 239/454] 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 07e07297..21ef3e50 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 4a993b01..e5d18f77 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1079,8 +1079,7 @@ moves_loop: // When in check, search starts from here /* && 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 240/454] 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 e5d18f77..9cdc7046 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 241/454] 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 9cdc7046..201cd974 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1055,7 +1055,7 @@ moves_loop: // When in check, search starts from here && !(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 242/454] 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 4741e722..cab7a7e5 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 243/454] 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 f71a8b34..7b6ddf4c 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 201cd974..37e3ff22 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1072,7 +1072,7 @@ moves_loop: // When in check, search starts from here // then that move is singular and should be extended. To verify this we do // a reduced search on all the other moves but the ttMove and if the // result is lower than ttValue minus a margin, then we will extend the ttMove. - if ( depth >= 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 244/454] 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 37e3ff22..0a2519b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ moves_loop: // When in check, search starts from here // 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 245/454] 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 cab7a7e5..b7585a17 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 3d7c75e5..05f79b45 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 246/454] 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 1ae6cb3a..a642357e 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 247/454] 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 05f79b45..bdd7bccb 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 248/454] 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 a642357e..ce35c630 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 249/454] 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 b585bc87..ecc3008a 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 7ade598f..7e5fcf4a 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 1cfebbe4..f899d761 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 250/454] 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 b7585a17..571172b2 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 44aea14e..1aa66a81 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 46da1e34..042bc2e9 100644 --- a/src/thread.h +++ b/src/thread.h @@ -65,6 +65,7 @@ public: 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 251/454] 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 ecc3008a..b585bc87 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 7e5fcf4a..7ade598f 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 f899d761..1cfebbe4 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 252/454] 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 faeb78ae..b0689d6d 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 253/454] 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 ce35c630..caab2979 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 254/454] 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 b585bc87..20ec2f12 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 7ade598f..13196ec2 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 972ef3e5..e7ce84f7 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 1cfebbe4..cbcc26f3 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 255/454] 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 0a2519b6..3d2bb422 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1131,7 +1131,8 @@ moves_loop: // When in check, search starts from here 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 256/454] 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 571172b2..a48e7dcb 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 bdd7bccb..5061ae13 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 257/454] 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 20ec2f12..89cfaad7 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 2a354a3c..69dfaad2 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 e7ce84f7..ff33cc79 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 cbcc26f3..3818e444 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 258/454] 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 21ef3e50..41b89705 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 a48e7dcb..3d84f482 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 5061ae13..401a6505 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 89cfaad7..985ee71a 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 13196ec2..44d8a7de 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 ff33cc79..cb1251c5 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 3818e444..40f2603d 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 259/454] 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 3d2bb422..676427f7 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here && !(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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } 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 260/454] 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 676427f7..ef47fd22 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 261/454] 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 3d84f482..fd2618a4 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 262/454] 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 401a6505..fc3746cf 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 263/454] 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 fd2618a4..e34fbf61 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 985ee71a..8d2acd18 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 264/454] 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 d563a1e1..0dd38047 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 d356ba2f..a3732a23 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 e34fbf61..c00b60b5 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 6041d642..806e9840 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 ae6d5c4b..03ded74a 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 265/454] 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 ef47fd22..c5b4332f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1139,7 +1139,7 @@ moves_loop: // When in check, search starts from here // 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 266/454] 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 c00b60b5..e82b066b 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 cb1251c5..eab7d258 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 267/454] 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 fc3746cf..aeb3c912 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 268/454] 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 e82b066b..0804cdd5 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 aeb3c912..ab52d30b 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 8d2acd18..322e3240 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 269/454] 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 0804cdd5..027cc3e3 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 ab52d30b..1cee4726 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 270/454] 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 b0689d6d..0a35d01b 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 271/454] 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 027cc3e3..a9fb7b81 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 272/454] 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 a9fb7b81..38f607cb 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 0a35d01b..2b66a475 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 273/454] 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 caab2979..00fd2005 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -927,9 +927,6 @@ make_v: // 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 @@ make_v: 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 274/454] 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 00fd2005..a453fa0f 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 af0894b2..a6ece8e2 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 275/454] 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 a453fa0f..3a620a78 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -939,7 +939,7 @@ make_v: 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 276/454] 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 1cee4726..459ea100 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 277/454] 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 a6ece8e2..3aa85943 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 322e3240..bac258e8 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 eab7d258..61f18aee 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 40f2603d..4db9be9f 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 278/454] 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 0dd38047..45f1bd3d 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 279/454] 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 3aa85943..dfbb1ac2 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 bac258e8..7ac5a1c0 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 61f18aee..4c93e3d1 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 4db9be9f..43707610 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 280/454] 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 c5b4332f..83fb722f 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1126,12 +1126,6 @@ moves_loop: // When in check, search starts from here && (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 281/454] 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 41b89705..d8f4d30e 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 38f607cb..0f458aa1 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 282/454] 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 83fb722f..7c839dfc 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 283/454] 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 7c839dfc..1d5bc5f7 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1122,11 +1122,6 @@ moves_loop: // When in check, search starts from here && (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 284/454] 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 3a620a78..1bd89353 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 @@ make_v: 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 285/454] 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 0f458aa1..1f8ba455 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 286/454] 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 1f8ba455..a3feb68e 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 287/454] 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 | 145 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 91 insertions(+), 57 deletions(-) diff --git a/.travis.yml b/.travis.yml index 45f1bd3d..12596f1e 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 a3feb68e..79c7333a 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) +# x86-32/64 + +ifeq ($(findstring x86-32,$(ARCH)),x86-32) arch = i386 bits = 32 -endif - -ifeq ($(ARCH),x86-32) - arch = i386 - bits = 32 - prefetch = yes + sse = yes mmx = yes - sse = yes -endif - -ifeq ($(ARCH),general-64) - arch = any -endif - -ifeq ($(ARCH),x86-64) +else arch = x86_64 - prefetch = yes + sse = yes + sse2 = yes +endif + +ifeq ($(findstring -sse,$(ARCH)),-sse) sse = yes endif -ifeq ($(ARCH),x86-64-sse3-popcnt) - arch = x86_64 - prefetch = yes - sse = yes +ifeq ($(findstring -popcnt,$(ARCH)),-popcnt) popcnt = yes endif -ifeq ($(ARCH),x86-64-ssse3) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -mmx,$(ARCH)),-mmx) + mmx = yes +endif + +ifeq ($(findstring -sse2,$(ARCH)),-sse2) sse = yes + sse2 = yes +endif + +ifeq ($(findstring -ssse3,$(ARCH)),-ssse3) + sse = yes + sse2 = yes ssse3 = yes endif -ifeq ($(ARCH),$(filter $(ARCH),x86-64-sse41-popcnt x86-64-modern)) - arch = x86_64 - prefetch = yes - popcnt = yes +ifeq ($(findstring -sse41,$(ARCH)),-sse41) sse = yes + sse2 = yes ssse3 = yes sse41 = yes endif -ifeq ($(ARCH),x86-64-avx2) - arch = x86_64 - prefetch = yes +ifeq ($(findstring -modern,$(ARCH)),-modern) popcnt = yes sse = yes + sse2 = yes + ssse3 = yes + sse41 = yes +endif + +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 288/454] 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 1d5bc5f7..7c839dfc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1122,6 +1122,11 @@ moves_loop: // When in check, search starts from here && (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 289/454] 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 1bd89353..c84d894f 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 290/454] 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 2b66a475..ec83c7c8 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 291/454] 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 459ea100..56a3dcad 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 292/454] 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 12596f1e..a029c4fc 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 d8f4d30e..c96f870a 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 79c7333a..b969ba04 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 293/454] 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 7c839dfc..ba13680c 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 294/454] 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 | 65 ++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index c84d894f..c66938d6 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -972,42 +972,47 @@ std::string Eval::trace(const Position& pos) { Value v; - if (Eval::useNNUE) - { - v = NNUE::evaluate(pos); - } - else - { - std::memset(scores, 0, sizeof(scores)); + std::memset(scores, 0, sizeof(scores)); - pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt + pos.this_thread()->contempt = SCORE_ZERO; // Reset any dynamic contempt - v = Evaluation(pos).value(); + 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); - } + 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"; + ss << "\nClassical evaluation: " << to_cp(v) << " (white side)\n"; + + if (Eval::useNNUE) + { + v = NNUE::evaluate(pos); + 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"; + 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 295/454] 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 b969ba04..74ef87b9 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 c4b55a48..75ef5d9a 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 296/454] 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 74ef87b9..b0274504 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 297/454] 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 7b6ddf4c..2cc88bf4 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 b0274504..74ef87b9 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 f531010c..80206b58 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 a899d879..29d8f66d 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 c66938d6..ce92db9a 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 0ef9926f..870a5e11 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 56a3dcad..80c436ac 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 eb4e05c0..8ad17b50 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 d74df4c3..3340f65c 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 96a44449..153d323e 100644 --- a/src/movepick.cpp +++ b/src/movepick.cpp @@ -182,7 +182,7 @@ top: --endMoves; ++stage; - /* fallthrough */ + [[fallthrough]]; case REFUTATION: if (select([&](){ return *cur != MOVE_NONE @@ -190,7 +190,7 @@ top: && pos.pseudo_legal(*cur); })) return *(cur - 1); ++stage; - /* fallthrough */ + [[fallthrough]]; case QUIET_INIT: if (!skipQuiets) @@ -203,7 +203,7 @@ top: } ++stage; - /* fallthrough */ + [[fallthrough]]; case QUIET: if ( !skipQuiets @@ -217,7 +217,7 @@ top: endMoves = endBadCaptures; ++stage; - /* fallthrough */ + [[fallthrough]]; case BAD_CAPTURE: return select([](){ return true; }); @@ -228,7 +228,7 @@ top: score(); ++stage; - /* fallthrough */ + [[fallthrough]]; case EVASION: return select([](){ return true; }); @@ -246,14 +246,14 @@ top: 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 f080935a..97ea5bec 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 868d0c8e..af0f6618 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 46e5d78b..02898547 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 a77050eb..5ce17277 100644 --- a/src/position.h +++ b/src/position.h @@ -113,6 +113,7 @@ public: 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 ba13680c..82d8bb9d 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 @@ moves_loop: // When in check, search starts from here 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 df4ba9b2..6d9c95ef 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 d6486320..bc0ee0a0 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 298/454] 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 299/454] 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 97ea5bec..4c0ad551 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 82d8bb9d..266e2db3 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1531,6 +1531,10 @@ moves_loop: // When in check, search starts from here { 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 @@ moves_loop: // When in check, search starts from here } // 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 300/454] 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 74ef87b9..3e1b7c35 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 301/454] 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 a029c4fc..c1e6d6df 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 3e1b7c35..228ea851 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 7ac5a1c0..94d0b5a9 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 302/454] 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 d494c27d..60a3a5f1 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 c177ca52..fdfd6769 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 @@ private: 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 73da41e2..1cd711b6 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 303/454] 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 266e2db3..2ca64a01 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1570,6 +1570,12 @@ moves_loop: // When in check, search starts from here [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 304/454] 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 c1e6d6df..092c7f53 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 228ea851..2e85a144 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 305/454] 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 2e85a144..703aa230 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 306/454] 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 dfbb1ac2..e6619089 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 79ca83ae..558a6b22 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 628add6e..88e384a3 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 99842eea..ee6a8df3 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 a9d8e5af..7bc905dc 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 02898547..fe89b753 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 5ce17277..d6f5c9fd 100644 --- a/src/position.h +++ b/src/position.h @@ -171,7 +171,6 @@ public: // Used by NNUE StateInfo* state() const; - const EvalList* eval_list() const; private: // Initialization helpers (used while setting up a position) @@ -186,9 +185,6 @@ private: 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 @@ private: 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 1cd711b6..5873c698 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 307/454] 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 2ca64a01..d6b611a3 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here [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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 308/454] 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 d6b611a3..cae8a684 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1526,7 +1526,7 @@ moves_loop: // When in check, search starts from here 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 309/454] 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 c96f870a..c00ab657 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 2cc88bf4..96a495ae 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 a3732a23..ab608409 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 703aa230..5f363f02 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 ce92db9a..67154751 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 e808068d..d701f5a7 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 00000000..32484ab5 --- /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 00000000..c19684d7 --- /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 fbad6622..f95db1c2 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 80c436ac..3fbdea35 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -132,6 +132,7 @@ public: } // 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 8ad17b50..68b9c884 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 e6619089..d6ac9894 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 ec83c7c8..5e747a7f 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 310/454] 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 5f363f02..9ae5a51c 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 311/454] 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 cae8a684..77447043 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here [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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 312/454] 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 77447043..a2342a3c 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 2554f3fb..79085189 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 313/454] 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 a2342a3c..b319dff5 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1585,7 +1585,7 @@ moves_loop: // When in check, search starts from here [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 314/454] 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 67154751..09f36513 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 b319dff5..e6e53e7c 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 315/454] 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 e6e53e7c..c676bd6d 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 20215b96..3dfe3e3e 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 @@ encode_remaining: 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 316/454] 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 9ae5a51c..340b3008 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 317/454] 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 c676bd6d..c15cd753 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 318/454] 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 c15cd753..b79fa6be 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1160,7 +1160,6 @@ moves_loop: // When in check, search starts from here // 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 1aa66a81..b46fce5e 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 042bc2e9..34b99015 100644 --- a/src/thread.h +++ b/src/thread.h @@ -54,7 +54,6 @@ public: 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 319/454] 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 3dfe3e3e..4d682f1a 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -223,7 +223,9 @@ public: *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 320/454] 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 0ea5ac72..482e9000 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 321/454] 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 3fbdea35..22070f0e 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 322/454] 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 22070f0e..3fbdea35 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 323/454] 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 b79fa6be..0d823c8e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1564,7 +1564,9 @@ moves_loop: // When in check, search starts from here } // 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 324/454] 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 0d823c8e..b5e190c8 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1196,7 +1196,7 @@ moves_loop: // When in check, search starts from here // 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 325/454] 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 b5e190c8..a7692841 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1168,13 +1168,6 @@ moves_loop: // When in check, search starts from here { 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 326/454] 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 d701f5a7..3da6a9fe 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 327/454] 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 a7692841..4aeadc28 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 @@ moves_loop: // When in check, search starts from here Move ttMove, move, bestMove; Depth ttDepth; Value bestValue, value, ttValue, futilityValue, futilityBase, oldAlpha; - bool ttHit, pvHit, givesCheck, captureOrPromotion; + bool pvHit, givesCheck, captureOrPromotion; int moveCount; if (PvNode) @@ -1453,13 +1453,13 @@ moves_loop: // When in check, search starts from here : 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 @@ moves_loop: // When in check, search starts from here } 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 79085189..f60da4a5 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 328/454] 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 09f36513..db8379da 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,12 +1015,16 @@ make_v: 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 329/454] 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 d6ac9894..ed138881 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 69dfaad2..26370710 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 43707610..2b6259c3 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 fe89b753..e6a760d2 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 330/454] 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 806e9840..ffb631a2 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 331/454] 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 db8379da..faf71d27 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,10 +1015,13 @@ make_v: 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 332/454] 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 4aeadc28..07c491b6 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 333/454] 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 3da6a9fe..c723bd8f 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 334/454] 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 07c491b6..c7d2efd4 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 335/454] 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 340b3008..54868b39 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 336/454] 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 c00ab657..198dfa5a 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 bc0ee0a0..3f3cc458 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 337/454] 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 c7d2efd4..17cd0a73 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1127,11 +1127,6 @@ moves_loop: // When in check, search starts from here && 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 338/454] 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 17cd0a73..9c5fb58b 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1056,7 +1056,6 @@ moves_loop: // When in check, search starts from here 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 339/454] 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 | 233 +++++++++++++++------------- 1 file changed, 125 insertions(+), 108 deletions(-) diff --git a/src/nnue/nnue_feature_transformer.h b/src/nnue/nnue_feature_transformer.h index 2b6259c3..e71ee60d 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]); + #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]; - #elif defined(USE_SSE2) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast<__m128i*>( - &accumulator.accumulation[perspective][i][0]); + 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]); - #elif defined(USE_MMX) - constexpr IndexType kNumChunks = kHalfDimensions / (kSimdWidth / 2); - auto accumulation = reinterpret_cast<__m64*>( - &accumulator.accumulation[perspective][i][0]); + // 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]); - #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) + 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]); + } + } + + 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 340/454] 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 9c5fb58b..22cb8577 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1151,7 +1151,7 @@ moves_loop: // When in check, search starts from here // 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 341/454] 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 | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index faf71d27..a9159477 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -1015,20 +1015,28 @@ make_v: 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; + Value v; - if ( useClassical - && Eval::useNNUE - && abs(v) * 16 < NNUEThreshold2 * (16 + pos.rule50_count())) - v = NNUE::evaluate(pos) * 5 / 4 + Tempo; + 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 342/454] 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 96a495ae..255ebce2 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 3fbdea35..d9bc47e3 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__) +#if defined(_WIN32) -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; -} - -#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) { - 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; +#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_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 68b9c884..bc48f303 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 ed138881..72d18200 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 5f0d1855..459a93de 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 60a3a5f1..dea7c712 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 fdfd6769..6aa066c5 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 @@ private: 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 343/454] 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 | 20 +++++++++----------- src/search.h | 1 - src/thread.cpp | 20 ++++++++++---------- src/uci.cpp | 2 +- src/ucioption.cpp | 4 ++-- 11 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/evaluate.cpp b/src/evaluate.cpp index a9159477..d3937823 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 c723bd8f..56354cf5 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 f95db1c2..e6dff918 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 72d18200..b5dcd992 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 459a93de..6cacf37e 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 7bc905dc..8afea186 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 22cb8577..edc020fd 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 @@ moves_loop: // When in check, search starts from here } else { - // Increase reduction for captures/promotions if late move and at low depth - if (depth < 8 && moveCount > 2) - 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++; + // 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 @@ moves_loop: // When in check, search starts from here [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 f60da4a5..72d43c31 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 b46fce5e..2fbf745d 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 3f3cc458..b63e55ad 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 5e747a7f..bb0b8311 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 344/454] 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 edc020fd..4650b157 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 345/454] 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 255ebce2..409d0a10 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 d9bc47e3..a16a6e90 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 346/454] 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 d3937823..1503be2d 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 347/454] 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 1503be2d..710898bc 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 348/454] 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 558a6b22..26198114 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 88e384a3..116157cc 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 ee6a8df3..52a83eec 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 e71ee60d..2f86d20a 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 349/454] 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 56354cf5..503aa975 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 350/454] 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 4650b157..e5f286e4 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 351/454] 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 710898bc..fe92f7d7 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 352/454] 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 503aa975..4b57a050 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 353/454] 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 e5f286e4..c7343ce8 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 354/454] 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 fe92f7d7..25e3bdc1 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 355/454] 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 03ded74a..03e9c9de 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 356/454] 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 c7343ce8..eaa79fb9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1049,15 +1049,6 @@ moves_loop: // When in check, search starts from here && 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 357/454] 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 af0f6618..a5102db8 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 358/454] 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 4b57a050..6a17f284 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 359/454] 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 25e3bdc1..c68577a3 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 360/454] 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 c68577a3..425ba6f8 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 361/454] 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 6a17f284..6a8603ad 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 362/454] 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 eaa79fb9..ab58ca64 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 @@ moves_loop: // When in check, search starts from here 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 34b99015..6a73423b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -73,6 +73,7 @@ public: 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 363/454] 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 ab58ca64..65ed9b73 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 @@ moves_loop: // When in check, search starts from here (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 6d9c95ef..da08f12d 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 364/454] 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 54868b39..87203547 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 365/454] 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 | 288 ++++++++++++++-------------- src/position.cpp | 17 +- 4 files changed, 157 insertions(+), 261 deletions(-) diff --git a/src/nnue/features/feature_set.h b/src/nnue/features/feature_set.h index 26198114..975824b6 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 26370710..a357d835 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 2f86d20a..f145c848 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,27 +219,142 @@ namespace Eval::NNUE { } private: - // Calculate cumulative value without using difference calculation - void RefreshAccumulator(const Position& pos) const { + void UpdateAccumulator(const Position& pos, const Color c) 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) { + #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 + + // 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; + } + + 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( + &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[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[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]); + } + + // 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]); + } + } + + #else + 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[i]) + { + const IndexType offset = kHalfDimensions * index; + + for (IndexType j = 0; j < kHalfDimensions; ++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; + + for (IndexType j = 0; j < kHalfDimensions; ++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]); - auto accTile = reinterpret_cast( - &accumulator.accumulation[perspective][i][j * kTileHeight]); - vec_t acc[kNumRegs]; - - for (unsigned k = 0; k < kNumRegs; ++k) + for (IndexType k = 0; k < kNumRegs; ++k) acc[k] = biasesTile[k]; - for (const auto index : active_indices[perspective]) { + for (const auto index : active) + { const IndexType offset = kHalfDimensions * index + j * kTileHeight; auto column = reinterpret_cast(&weights_[offset]); @@ -268,18 +362,22 @@ namespace Eval::NNUE { 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[perspective][i], biases_, + std::memcpy(accumulator.accumulation[c][0], biases_, kHalfDimensions * sizeof(BiasType)); - for (const auto index : active_indices[perspective]) { + for (const auto index : active) + { const IndexType offset = kHalfDimensions * index; for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] += weights_[offset + j]; + accumulator.accumulation[c][0][j] += weights_[offset + j]; } #endif } @@ -287,106 +385,6 @@ namespace Eval::NNUE { #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; - } - - 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 }) { - 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]); - } - } - - 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)); - // Difference calculation for the deactivated features - for (const auto index : removed_indices[perspective]) { - const IndexType offset = kHalfDimensions * index; - - for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] -= weights_[offset + j]; - } - } - { // Difference calculation for the activated features - for (const auto index : added_indices[perspective]) { - const IndexType offset = kHalfDimensions * index; - - for (IndexType j = 0; j < kHalfDimensions; ++j) - accumulator.accumulation[perspective][i][j] += weights_[offset + j]; - } - } - } - #endif - - accumulator.computed_accumulation = true; } using BiasType = std::int16_t; diff --git a/src/position.cpp b/src/position.cpp index e6a760d2..b707293d 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 366/454] 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 425ba6f8..030d1017 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 367/454] 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 6a8603ad..6e5db6a3 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 368/454] 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 153d323e..f5e02385 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 369/454] 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 6e5db6a3..6bec27db 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 370/454] 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 | 478 +++++++++++++++++++++++------ 1 file changed, 384 insertions(+), 94 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 94d0b5a9..f0292e45 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 - const __m128i kZeros = _mm_setzero_si128(); - #else - const __m128i kOnes = _mm_set1_epi16(1); - #endif + + auto output = reinterpret_cast(buffer); const auto input_vector = reinterpret_cast(input); - #elif defined(USE_MMX) + // 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 + 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) +#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 371/454] 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 030d1017..4ade46fa 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 372/454] 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 65ed9b73..743449fa 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1565,7 +1565,7 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 373/454] 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 bc48f303..682ef816 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 b5dcd992..b0ed7d2f 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 44d8a7de..7f6d67bf 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 8afea186..a9664262 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 f145c848..c3f012e4 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 b707293d..5ce7da22 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 743449fa..12c32194 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 @@ moves_loop: // When in check, search starts from here 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 5873c698..bf692f7e 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 374/454] 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 12c32194..6e37fba1 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 375/454] 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 a5102db8..fde70ba5 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 376/454] 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 f0292e45..47c9c488 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 c3f012e4..f49777b5 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 377/454] 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 6bec27db..06c66f71 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 378/454] 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 6e37fba1..b5b93bf0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1525,7 +1525,7 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } // 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 379/454] 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 198dfa5a..f0356090 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 b5b93bf0..56b56733 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1169,6 +1169,9 @@ moves_loop: // When in check, search starts from here 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 380/454] 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 f0356090..f30be4de 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 56b56733..66ef5043 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1161,7 +1161,7 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 381/454] 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 4ade46fa..34ebe6c3 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 382/454] 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 f30be4de..71b718b8 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 34ebe6c3..1a8cf662 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 a16a6e90..f2bce6b0 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 f49777b5..85bc2bc8 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 fde70ba5..68aaf331 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 66ef5043..78a1f7b6 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1058,7 +1058,7 @@ moves_loop: // When in check, search starts from here && 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 bf692f7e..8506b06e 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 383/454] 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 1a8cf662..3d887119 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 78a1f7b6..7c797bef 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here - 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 384/454] 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 71b718b8..b31a36e9 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 47c9c488..caf315b2 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 385/454] 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 3d887119..90d11a00 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 386/454] 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 b0ed7d2f..382d8ff9 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 116157cc..29322f04 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 52a83eec..708fd7ea 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 a9664262..f9ff2bc8 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 387/454] 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 90d11a00..fd51ad53 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 388/454] 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 7c797bef..d854cd95 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1257,9 +1257,6 @@ moves_loop: // When in check, search starts from here int bonus = value > alpha ? stat_bonus(newDepth) : -stat_bonus(newDepth); - if (move == ss->killers[0]) - bonus += bonus / 4; - update_continuation_histories(ss, movedPiece, to_sq(move), bonus); } } From 6c429c4d6527a8e88d2ad1e7bfc8f4bd0ec05729 Mon Sep 17 00:00:00 2001 From: Unai Corzo Date: Sat, 28 Nov 2020 16:32:52 +0100 Subject: [PATCH 389/454] 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 d854cd95..99d1cb0e 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1554,7 +1554,6 @@ moves_loop: // When in check, search starts from here // 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 390/454] 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 409d0a10..eb7aa5a7 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 391/454] 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 c8be2198..7e005a28 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 fd51ad53..6ee53621 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 3340f65c..cc1518a0 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 68aaf331..b6d29003 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 5ce7da22..07ce0a7c 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 d6f5c9fd..02156448 100644 --- a/src/position.h +++ b/src/position.h @@ -99,7 +99,6 @@ public: 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 @@ private: 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 392/454] 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 99d1cb0e..52541868 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1222,10 +1222,6 @@ moves_loop: // When in check, search starts from here } 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 393/454] 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 6ee53621..a1b04316 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 06c66f71..7dbc35de 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 394/454] 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 f5e02385..9ca1a21f 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 4c0ad551..7fe98e49 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -123,10 +123,12 @@ public: 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 @@ private: 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 52541868..ab559696 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 2fbf745d..bfcdb65e 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 6a73423b..c05fa941 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,6 +69,7 @@ public: 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 395/454] 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 a1b04316..ef33adf5 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 ab559696..ba9265dd 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here { 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } } + // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here } } 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here thisThread->mainHistory[us][from_to(move)] << bonus; update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus); + // 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 396/454] 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 caf315b2..0e0515f9 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 397/454] 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 ef33adf5..c507aa06 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 870a5e11..f77972e3 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 80d01655..28da59db 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 b6d29003..16dbf27a 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 398/454] 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 | 353 ++++++++++++++++------------- 1 file changed, 198 insertions(+), 155 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 0e0515f9..a715ca85 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) { + __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]); -#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 + 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]; - for (IndexType j = kStart; j < kNumChunks512; ++j) + 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 { + __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]); -#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) + 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) { + __m512i sum0 = _mm512_setzero_si512(); + 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) + 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 { + __m256i sum0 = _mm256_setzero_si256(); + 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) + 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]); + __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]); -#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) + 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]; -#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_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); @@ -553,25 +585,15 @@ namespace Eval::NNUE::Layers { } else if constexpr (kOutputDimensions == 1) { + __m256i sum0 = _mm256_setzero_si256(); + 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) + 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]) { - const __m128i in = input_vector[j]; + for (; j < (int)kNumChunks - 1; j += 2) + { + const __m128i in0 = input_vector[j]; + const __m128i in1 = input_vector[j + 1]; - 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_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]; + + 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 399/454] 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 9ca1a21f..f5e02385 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 7fe98e49..4c0ad551 100644 --- a/src/movepick.h +++ b/src/movepick.h @@ -123,12 +123,10 @@ public: 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 @@ private: 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 ba9265dd..e825fce7 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 bfcdb65e..2fbf745d 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 c05fa941..6a73423b 100644 --- a/src/thread.h +++ b/src/thread.h @@ -69,7 +69,6 @@ public: 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 400/454] 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 e825fce7..cdbccb4c 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1188,7 +1188,7 @@ moves_loop: // When in check, search starts from here 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 401/454] 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 c507aa06..dd5d23b2 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 16dbf27a..ed83fde7 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 402/454] 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 cdbccb4c..e272c10b 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 403/454] 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 dd5d23b2..c945cf53 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 404/454] 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 c945cf53..7671f605 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 405/454] 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 e272c10b..60a002a9 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,6 +1172,7 @@ moves_loop: // When in check, search starts from here || 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 406/454] 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 60a002a9..c6db82ba 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1172,7 +1172,7 @@ moves_loop: // When in check, search starts from here || 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 407/454] 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 4c0ad551..f8472c6e 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 c6db82ba..5f545ef6 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 408/454] 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 5f545ef6..b96efad0 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1142,12 +1142,6 @@ moves_loop: // When in check, search starts from here && 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 409/454] 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 b96efad0..e2c3f584 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here [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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 410/454] 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 ed83fde7..d3d2ea0f 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 411/454] 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 a715ca85..ab2beab7 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]]; + 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) + { + 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]; + } - return pSum > 258 || nSum < -258; - }; + 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; + } + } - 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); - } + // 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) - - constexpr IndexType kNumChunks512 = kPaddedInputDimensions / (kSimdWidth * 2); - constexpr IndexType kNumChunks256 = kPaddedInputDimensions / kSimdWidth; - - const auto output = reinterpret_cast(buffer); - - // 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 % 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 (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); - } - + 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 kNumChunks = kPaddedInputDimensions / kSimdWidth; +#if defined (USE_SSSE3) const auto output = reinterpret_cast(buffer); - const auto input_vector = reinterpret_cast(input); + const auto input_vector = 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 % 4 == 0) + if constexpr (kOutputDimensions % kOutputSimdWidth == 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; + constexpr IndexType kNumChunks = kPaddedInputDimensions / 4; - const __m128i bias = *reinterpret_cast(&biases_[i]); - __m128i* outptr = reinterpret_cast<__m128i*>(&output[i]); + const auto input32 = reinterpret_cast(input); + vec_t* outptr = reinterpret_cast(output); + std::memcpy(output, biases_, kOutputDimensions * sizeof(OutputType)); - __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 (int i = 0; i < (int)kNumChunks - 3; 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]); - } + 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]); } - 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(); + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; - const auto row0 = reinterpret_cast(&weights_[0]); + vec_t sum0 = vec_setzero(); - for (IndexType j = 0; j < kNumChunks; ++j) - { - const __m256i in = input_vector[j]; + const auto row0 = reinterpret_cast(&weights_[0]); - 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; - - 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 (int j = 0; j < (int)kNumChunks; ++j) { - for (; j < (int)kNumChunks - 1; j += 2) - { - const __m128i in0 = input_vector[j]; - const __m128i in1 = input_vector[j + 1]; + const vec_t in = input_vector[j]; - 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]; - - 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 412/454] 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 e2c3f584..9ea77b3a 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1039,8 +1039,20 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 413/454] 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 ffb631a2..7cb04382 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 bbe8e9a7..b640eabb 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 80206b58..841aa0b6 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 29d8f66d..95591fc4 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 7e005a28..1489a36b 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 1351d88a..860cc863 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 7671f605..c137fab5 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 7dbc35de..8beca2d0 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 e6dff918..ef46d0b5 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 f77972e3..36b6132c 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 28da59db..be26425f 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 f2bce6b0..48e20a39 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 682ef816..7b551ade 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 cc1518a0..e017d8fe 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 fb616d00..85887a64 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 f5e02385..8bface8a 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 f8472c6e..98629783 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 9216bd41..a0fe2e0a 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 382d8ff9..fb4a5021 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 6cacf37e..c30d7c01 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 975824b6..77d2220f 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 d00a35df..b0073b8b 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 29322f04..b52a45f2 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 708fd7ea..d203dc22 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 d9ad680a..ca3ebee5 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 ab2beab7..34777ef6 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 7f6d67bf..3ed41ee5 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 afca14c8..efdf0725 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 a357d835..6b4390f9 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 91cdc4bd..ad5be006 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 f9ff2bc8..33e58745 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 85bc2bc8..2641321e 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 d3d2ea0f..de47570e 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 5499826e..888bf990 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 07ce0a7c..837847b4 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 02156448..8509029d 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 eb36e75e..bf87237a 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 9ea77b3a..d2a64176 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 72d43c31..3bf3e9ae 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 4d682f1a..97d848a2 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 b998989b..cefd39ce 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 2fbf745d..a12c0bcc 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 6a73423b..585f2088 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 75ef5d9a..a0e4d199 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 da08f12d..fc4fbaac 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 5ad72b32..55a68de4 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 dea7c712..d1ba9ebb 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 6aa066c5..5f9525a6 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 e94f67f8..424bdac8 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 1489fa32..ef418968 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 8506b06e..1832b302 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 b63e55ad..b3017e91 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 eb0b390b..edcfcade 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 bb0b8311..03f377b3 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 414/454] 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 b31a36e9..d170364e 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 e017d8fe..855a203e 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 415/454] 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 | 40 ++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/nnue/layers/affine_transform.h b/src/nnue/layers/affine_transform.h index 34777ef6..adf152ee 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]; + constexpr IndexType kNumChunks = kPaddedInputDimensions / kSimdWidth; + const auto input_vector256 = reinterpret_cast(input); - vec_add_dpbusd_32(sum0, in, row0[j]); + __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]); - output[0] = vec_hadd(sum0, biases_[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]); + } } #else From b1bb376c3cc9a2e197759b3e6f0d365c8aae7e72 Mon Sep 17 00:00:00 2001 From: Vizvezdenec Date: Sun, 10 Jan 2021 20:24:37 +0300 Subject: [PATCH 416/454] 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 d2a64176..e405a373 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1159,7 +1159,7 @@ moves_loop: // When in check, search starts from here || 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 417/454] 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 d170364e..f3ae5f09 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 855a203e..5ce2de7c 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 8bface8a..9cb8447f 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 98629783..5232ee4d 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 837847b4..12b1bd9a 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 8509029d..93e0f91d 100644 --- a/src/position.h +++ b/src/position.h @@ -113,7 +113,7 @@ public: 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 e405a373..3da3d04e 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 @@ moves_loop: // When in check, search starts from here // Check extension (~2 Elo) else if ( givesCheck - && (pos.is_discovery_check_on_king(~us, move) || pos.see_ge(move))) + && (pos.is_discovered_check_on_king(~us, move) || pos.see_ge(move))) extension = 1; // Last captures extension @@ -1541,7 +1541,7 @@ moves_loop: // When in check, search starts from here && 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 1832b302..d270384e 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 418/454] 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 5ce2de7c..aaa1ff88 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 419/454] 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 aaa1ff88..fafa65ee 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 420/454] 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 3da3d04e..7abffb87 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1233,7 +1233,13 @@ moves_loop: // When in check, search starts from here 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 421/454] 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 c137fab5..76266937 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 bf87237a..e2107240 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 7abffb87..6cc115fc 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1233,10 +1233,10 @@ moves_loop: // When in check, search starts from here 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 422/454] 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 fafa65ee..88c31cfa 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 423/454] 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 de47570e..5d6770ed 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 424/454] 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 f3ae5f09..d2b26469 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 97d848a2..36234786 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 425/454] 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 00000000..8981efca --- /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 426/454] 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 76266937..6bd3c08b 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 12b1bd9a..8c97160b 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 93e0f91d..928366bc 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 427/454] 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 | 40 +++++++++++++++++++++++----------------- src/psqt.h | 38 ++++++++++++++++++++++++++++++++++++++ src/syzygy/tbprobe.cpp | 4 ++-- src/tune.h | 6 +++--- 11 files changed, 100 insertions(+), 59 deletions(-) create mode 100644 src/psqt.h diff --git a/src/evaluate.cpp b/src/evaluate.cpp index 6bd3c08b..0b211261 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 @@ make_v: 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 ef46d0b5..ef662468 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 36b6132c..e76641d1 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 88c31cfa..14df1f05 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 5232ee4d..ea599cda 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 8c97160b..6a5d09ee 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 928366bc..3624e29e 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 @@ private: 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 e2107240..46605d52 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,11 +98,14 @@ 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 + + +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. @@ -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]); + 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]; - } + 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 00000000..8b4fd6eb --- /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 36234786..115815e1 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 ef418968..c2cd0c97 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 428/454] 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 6a5d09ee..ad1865f0 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 429/454] 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 ad1865f0..954e5ffd 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 430/454] 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 14df1f05..c9d6a90d 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 431/454] 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 954e5ffd..826e847f 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 432/454] 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 9cb8447f..f5b27496 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 433/454] 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 826e847f..35e307ef 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 434/454] 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 5d6770ed..cd4d4e45 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 46605d52..cfade295 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 435/454] 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 6cc115fc..80c3e4c2 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 436/454] 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 87203547..55139a1f 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 437/454] 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 55139a1f..827ce6bb 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 438/454] 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 827ce6bb..1ff03f83 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 439/454] 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 d2b26469..5f21c048 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 d1ba9ebb..cb5af5c8 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 5f9525a6..a750b6c4 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 440/454] 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 80c3e4c2..d77ab691 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,6 +1025,14 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 441/454] 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 482e9000..f5347ea1 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 442/454] 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 1ff03f83..eb32758f 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 0b211261..d55ef695 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 f5b27496..0ceeb8ea 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 35e307ef..2eb30ca0 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 d77ab691..b5d21b9d 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1025,12 +1025,12 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 443/454] 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 eb7aa5a7..fdc6fd61 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. -## What to expect from Syzygybases? +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 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 444/454] 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 48e20a39..ebc7c028 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 445/454] 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 ebc7c028..48e20a39 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 446/454] 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 b5d21b9d..8f01f785 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 @@ moves_loop: // When in check, search starts from here && (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - + (*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 @@ moves_loop: // When in check, search starts from here || 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 @@ moves_loop: // When in check, search starts from here + (*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)] + (*contHist[3])[movedPiece][to_sq(move)] - - 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 @@ moves_loop: // When in check, search starts from here // 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 447/454] 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 8f01f785..c33bc914 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here } } - // 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 @@ moves_loop: // When in check, search starts from here [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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 448/454] 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 c33bc914..b3bd2f03 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 @@ moves_loop: // When in check, search starts from here // 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 @@ moves_loop: // When in check, search starts from here 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 @@ moves_loop: // When in check, search starts from here 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 3bf3e9ae..5e51c18e 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 449/454] 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 5f21c048..3ef7d1b1 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 841aa0b6..8146ce97 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 95591fc4..c9555b6b 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 48e20a39..fe920140 100644 --- a/src/misc.cpp +++ b/src/misc.cpp @@ -138,7 +138,7 @@ public: /// 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 7b551ade..7eb75bc7 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 2eb30ca0..8f9b7eee 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 3624e29e..e9803756 100644 --- a/src/position.h +++ b/src/position.h @@ -87,7 +87,7 @@ public: // 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 450/454] 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 | 18 +++++++++--------- 56 files changed, 199 insertions(+), 57 deletions(-) diff --git a/src/benchmark.cpp b/src/benchmark.cpp index 7cb04382..7945a453 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 b640eabb..ece9ec72 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 8146ce97..a2021449 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 c9555b6b..e14fe0df 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 1489a36b..a44d3a1c 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 860cc863..146111b9 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 d55ef695..d43b8fa7 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 8beca2d0..6210bd58 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 ef662468..62e0ed52 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 e76641d1..84d7a4bd 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 be26425f..26535a53 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 fe920140..834e909b 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 7eb75bc7..f834e470 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 c9d6a90d..51df6d07 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 85887a64..3f895f05 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 @@ private: ExtMove moveList[MAX_MOVES], *last; }; +} // namespace Stockfish + #endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp index 0ceeb8ea..4ff4cff4 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 @@ top: assert(false); return MOVE_NONE; // Silence warning } + +} // namespace Stockfish diff --git a/src/movepick.h b/src/movepick.h index ea599cda..c76d4957 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 @@ private: 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 a0fe2e0a..a6768204 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 fb4a5021..5416f13e 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 c30d7c01..24aa6cc0 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 77d2220f..a3fea9c0 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 b0073b8b..118ec953 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 b52a45f2..ac6317e7 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 d203dc22..2461acb7 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 ca3ebee5..9f03993b 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 adf152ee..d2713c5a 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 3ed41ee5..a10e3e48 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 efdf0725..43b06eec 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 6b4390f9..55fafa13 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 ad5be006..1680368e 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 33e58745..09a152a5 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 2641321e..1e0b0e6d 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 cd4d4e45..9a0610a0 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 888bf990..124619d6 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 8f9b7eee..17b165b9 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 e9803756..4ab3761f 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 cfade295..33a3e00c 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 8b4fd6eb..7abb1483 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 b3bd2f03..4e2ec095 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 5e51c18e..6f9fbd05 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 115815e1..5cfd38e5 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 cefd39ce..56734af9 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 a12c0bcc..3ef73dfa 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 585f2088..2b3dea0d 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 @@ private: 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 a0e4d199..a21674cc 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 @@ public: 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 fc4fbaac..f742d1e4 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 55a68de4..b1878d65 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 @@ private: extern TimeManagement Time; +} // namespace Stockfish + #endif // #ifndef TIMEMAN_H_INCLUDED diff --git a/src/tt.cpp b/src/tt.cpp index cb5af5c8..1f495ca9 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 a750b6c4..d915d92e 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 @@ private: extern TranspositionTable TT; +} // namespace Stockfish + #endif // #ifndef TT_H_INCLUDED diff --git a/src/tune.cpp b/src/tune.cpp index 424bdac8..d9618efc 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 c2cd0c97..c904c09d 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 @@ public: #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 d270384e..efebce1a 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 b3017e91..47a8824e 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 edcfcade..d3160109 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 03f377b3..d59c0100 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 03e9c9de..bfb50e94 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: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:TranspositionTable::probe -race:TranspositionTable::hashfull +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 451/454] 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 834e909b..7600fc11 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 452/454] 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 47a8824e..051ff2e0 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 453/454] 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 51df6d07..c5d76afa 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 454/454] 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 3ef7d1b1..662cc6ec 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 4ab3761f..a7654aa1 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 4e2ec095..fa592a85 100644 --- a/src/search.cpp +++ b/src/search.cpp @@ -1575,15 +1575,13 @@ moves_loop: // When in check, search starts from here 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;