From 5eeca7392ee90b7a43da69e647e3d596e42992fd Mon Sep 17 00:00:00 2001 From: anematode Date: Thu, 2 Apr 2026 20:29:20 +0200 Subject: [PATCH] Update NNUE architecture to SFNNv14 and net nn-7bf13f9655c8.nnue [STC](https://tests.stockfishchess.org/tests/live_elo/69cb991bc025c305b7daa219) LLR: 2.97 (-2.94,2.94) <0.00,2.00> Total: 47520 W: 12558 L: 12219 D: 22743 Ptnml(0-2): 190, 5474, 12102, 5795, 199 [LTC](https://tests.stockfishchess.org/tests/live_elo/69cc6b42731dfb72d2e92128) LLR: 2.94 (-2.94,2.94) <0.50,2.50> Total: 123756 W: 31996 L: 31497 D: 60263 Ptnml(0-2): 81, 13512, 34221, 13955, 109 Adds features for pawns pushing against other pawns. For indexing, we use the existing threats feature set by adding the forward square to the squares threatened by a pawn. This threat is only enabled when another pawn occupies that square. We then add appropriate logic changes in `update_piece_threats` and `features/full_threats.cpp`. The wonderful Ciekce (of Stormphrax) and peregrine (of Reckless) also had similar ideas with adding forward pawn movements to TI, and I appreciate their giving me motivation to figure out `nnue-pytorch` and test it. closes https://github.com/official-stockfish/Stockfish/pull/6694 Bench: 2926703 --- src/bitboard.h | 15 +++++++++++++++ src/evaluate.h | 2 +- src/nnue/features/full_threats.cpp | 22 +++++++++++++++++++--- src/nnue/features/full_threats.h | 2 +- src/position.cpp | 21 +++++++++++++++++++-- 5 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/bitboard.h b/src/bitboard.h index 7d36b0a62..50eb11864 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -155,6 +155,9 @@ constexpr Bitboard pawn_attacks_bb(Bitboard b) { : shift(b) | shift(b); } +constexpr Bitboard pawn_single_push_bb(Color c, Bitboard b) { + return c == WHITE ? shift(b) : shift(b); +} // Returns a bitboard representing an entire line (from board edge // to board edge) that intersects the two given squares. If the given squares @@ -397,6 +400,18 @@ inline constexpr auto PseudoAttacks = []() constexpr { return attacks; }(); +inline constexpr auto PawnPushOrAttacks = []() constexpr { + std::array, COLOR_NB> attacks{}; + + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + { + attacks[WHITE][s1] = pawn_single_push_bb(WHITE, square_bb(s1)) | PseudoAttacks[WHITE][s1]; + attacks[BLACK][s1] = pawn_single_push_bb(BLACK, square_bb(s1)) | PseudoAttacks[BLACK][s1]; + } + + return attacks; +}(); + // Returns the pseudo attacks of the given piece type // assuming an empty board. diff --git a/src/evaluate.h b/src/evaluate.h index 4af7093e0..a4a56d9f2 100644 --- a/src/evaluate.h +++ b/src/evaluate.h @@ -33,7 +33,7 @@ namespace Eval { // for the build process (profile-build and fishtest) to work. Do not change the // name of the macro or the location where this macro is defined, as it is used // in the Makefile/Fishtest. -#define EvalFileDefaultNameBig "nn-9a0cc2a62c52.nnue" +#define EvalFileDefaultNameBig "nn-7bf13f9655c8.nnue" #define EvalFileDefaultNameSmall "nn-47fc8b7fff06.nnue" namespace NNUE { diff --git a/src/nnue/features/full_threats.cpp b/src/nnue/features/full_threats.cpp index 03ad158e6..6cae5d0ed 100644 --- a/src/nnue/features/full_threats.cpp +++ b/src/nnue/features/full_threats.cpp @@ -21,6 +21,7 @@ #include "full_threats.h" #include +#include #include #include #include @@ -72,7 +73,7 @@ constexpr auto make_piece_indices_piece() { for (Square from = SQ_A1; from <= SQ_H8; ++from) { - Bitboard attacks = PseudoAttacks[C][from]; + Bitboard attacks = PawnPushOrAttacks[C][from]; for (Square to = SQ_A1; to <= SQ_H8; ++to) { @@ -135,8 +136,8 @@ constexpr auto init_threat_offsets() { else if (from >= SQ_A2 && from <= SQ_H7) { - Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb(square_bb(from)) - : pawn_attacks_bb(square_bb(from)); + Bitboard attacks = + (pieceIdx < 8) ? PawnPushOrAttacks[WHITE][from] : PawnPushOrAttacks[BLACK][from]; cumulativePieceOffset += constexpr_popcount(attacks); } } @@ -209,6 +210,7 @@ inline sf_always_inline IndexType FullThreats::make_index( void FullThreats::append_active_indices(Color perspective, const Position& pos, IndexList& active) { Square ksq = pos.square(perspective); Bitboard occupied = pos.pieces(); + Bitboard pawns = pos.pieces(PAWN); for (Color color : {WHITE, BLACK}) { @@ -248,6 +250,20 @@ void FullThreats::append_active_indices(Color perspective, const Position& pos, if (index < Dimensions) active.push_back(index); } + + // Set of pawns which are prevented from movement by a pawn in front of them + Bitboard pushers = pawn_single_push_bb(~c, pawns) & pos.pieces(c, PAWN); + while (pushers) + { + Square from = pop_lsb(pushers); + Square to = from + pawn_push(c); + Piece attacked = pos.piece_on(to); + assert(type_of(attacked) == PAWN); + + IndexType index = make_index(perspective, attacker, from, to, attacked, ksq); + if (index < Dimensions) + active.push_back(index); + } } else { diff --git a/src/nnue/features/full_threats.h b/src/nnue/features/full_threats.h index 76f5b74c6..4a39c6648 100644 --- a/src/nnue/features/full_threats.h +++ b/src/nnue/features/full_threats.h @@ -42,7 +42,7 @@ class FullThreats { static constexpr std::uint32_t HashValue = 0x8f234cb8u; // Number of feature dimensions - static constexpr IndexType Dimensions = 60144; + static constexpr IndexType Dimensions = 60720; // clang-format off // Orient a square according to perspective (rotates by 180 for black) diff --git a/src/position.cpp b/src/position.cpp index d7918e453..78ebac35c 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1265,8 +1265,25 @@ void Position::update_piece_threats(Piece pc, Bitboard threatened = attacks_bb(pc, s, occupied) & occupiedNoK; Bitboard incoming_threats = - (PseudoAttacks[KNIGHT][s] & knights) | (attacks_bb(s, WHITE) & blackPawns) - | (attacks_bb(s, BLACK) & whitePawns) | (PseudoAttacks[KING][s] & kings); + (PseudoAttacks[KNIGHT][s] & knights) | (PseudoAttacks[KING][s] & kings); + + // Compute both incoming and outgoing pawn threats. Incoming pawn pushers are only + // added if 'pc' is a pawn. + if (type_of(pc) == PAWN) + { + Bitboard whiteAttacks = PawnPushOrAttacks[WHITE][s]; + Bitboard blackAttacks = PawnPushOrAttacks[BLACK][s]; + + threatened |= (color_of(pc) == WHITE ? whiteAttacks : blackAttacks) & pieces(PAWN); + + incoming_threats |= whiteAttacks & blackPawns; + incoming_threats |= blackAttacks & whitePawns; + } + else + { + incoming_threats |= + (attacks_bb(s, WHITE) & blackPawns) | (attacks_bb(s, BLACK) & whitePawns); + } #ifdef USE_AVX512ICL if constexpr (PutPiece)