diff --git a/src/Makefile b/src/Makefile
index 1cd017b18..5329ebbe4 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -67,7 +67,7 @@ BINDIR = $(PREFIX)/bin
PGOBENCH = $(RUN_PREFIX) ./$(EXE) bench
### Source and object files
-SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
+SRCS = attacks.cpp benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
misc.cpp movegen.cpp movepick.cpp position.cpp \
search.cpp thread.cpp timeman.cpp tt.cpp uci.cpp ucioption.cpp tune.cpp syzygy/tbprobe.cpp \
nnue/nnue_accumulator.cpp nnue/nnue_misc.cpp nnue/network.cpp \
@@ -76,7 +76,7 @@ SRCS = benchmark.cpp bitboard.cpp evaluate.cpp main.cpp \
OTHER_SRCS = universal/entry_x86.cpp universal/entry_arm64.cpp universal/nnue_embed.cpp
-HEADERS = benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
+HEADERS = attacks.h benchmark.h bitboard.h evaluate.h misc.h movegen.h movepick.h history.h \
nnue/nnue_misc.h nnue/features/half_ka_v2_hm.h nnue/features/full_threats.h \
nnue/layers/affine_transform.h nnue/layers/affine_transform_sparse_input.h \
nnue/layers/clipped_relu.h nnue/layers/sqr_clipped_relu.h nnue/nnue_accumulator.h \
diff --git a/src/attacks.cpp b/src/attacks.cpp
new file mode 100644
index 000000000..ae112287b
--- /dev/null
+++ b/src/attacks.cpp
@@ -0,0 +1,239 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ 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 "attacks.h"
+
+#include
+
+#include "misc.h"
+
+namespace Stockfish::Attacks {
+
+namespace {
+
+Bitboard LineBB[SQUARE_NB][SQUARE_NB];
+Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
+Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
+
+alignas(64) Magic Magics[SQUARE_NB][2];
+
+}
+
+#ifdef USE_PEXT
+using MagicMask = uint16_t;
+#else
+using MagicMask = Bitboard;
+#endif
+
+#ifdef USE_HYPERBOLA_QUINT
+static Bitboard line_mask(Square sq, Direction d1, Direction d2) {
+ Bitboard mask = 0, dest;
+ for (Direction d : {d1, d2})
+ {
+ Square s = sq;
+ while ((dest = safe_destination(s, d)))
+ {
+ mask |= dest;
+ s += d;
+ }
+ }
+ return mask;
+}
+
+static void init_magics(Magic magics[][2]) {
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ Magic& rook = magics[s][ROOK - BISHOP];
+ rook.mask1 = line_mask(s, NORTH, SOUTH);
+ rook.mask2 = line_mask(s, EAST, WEST);
+
+ Magic& bishop = magics[s][BISHOP - BISHOP];
+ bishop.mask1 = line_mask(s, NORTH_EAST, SOUTH_WEST);
+ bishop.mask2 = line_mask(s, NORTH_WEST, SOUTH_EAST);
+
+ rook.r = bishop.r = square_bb(s) * 2;
+ rook.rr = bishop.rr = square_bb(Square(63 - int(s))) * 2;
+ }
+}
+
+#else
+
+namespace {
+[[maybe_unused]] constexpr Bitboard constexpr_pext(Bitboard b, Bitboard m) {
+ Bitboard result = 0, bit = 0;
+ while (m)
+ {
+ Bitboard last = m & -m;
+ result |= bool(b & last) << bit++;
+ m ^= last;
+ }
+ return result;
+}
+
+ #ifdef USE_COMPTIME_ATTACKS
+constexpr
+ #endif
+ void
+ init_magics(PieceType pt,
+ MagicMask table[],
+ Magic magics[][2],
+ [[maybe_unused]] bool tableAlreadyInit) {
+ #if !defined(USE_COMPTIME_ATTACKS)
+ tableAlreadyInit = false;
+ #endif
+
+ #ifndef USE_PEXT
+ int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
+ {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
+
+ Bitboard occupancy[4096];
+ int epoch[4096] = {}, cnt = 0;
+ Bitboard reference[4096] = {};
+ #endif
+ int size = 0;
+
+ for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ {
+ Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
+
+ Magic& m = magics[s][pt - BISHOP];
+ Bitboard attacks = sliding_attack(pt, s, 0);
+ m.mask = attacks & ~edges;
+ #ifdef USE_PEXT
+ m.pseudoAttacks = attacks;
+ #else
+ m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
+ #endif
+ m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
+ size = 0;
+
+ Bitboard b = 0;
+ [[maybe_unused]] Bitboard prevSliding = -1;
+ do
+ {
+ #ifdef USE_PEXT
+ if (!tableAlreadyInit)
+ {
+ Bitboard sliding = sliding_attack(pt, s, b);
+ m.attacks[size] =
+ sliding != prevSliding ? constexpr_pext(sliding, attacks) : m.attacks[size - 1];
+ prevSliding = sliding;
+ }
+ #else
+ occupancy[size] = b;
+ reference[size] = sliding_attack(pt, s, b);
+ #endif
+
+ size++;
+ b = (b - m.mask) & m.mask;
+ } while (b);
+
+ #ifndef USE_PEXT
+ PRNG rng(seeds[Is64Bit][rank_of(s)]);
+
+ for (int i = 0; i < size;)
+ {
+ for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;)
+ m.magic = rng.sparse_rand();
+
+ for (++cnt, i = 0; i < size; ++i)
+ {
+ unsigned idx = m.index(occupancy[i]);
+
+ if (epoch[idx] < cnt)
+ {
+ epoch[idx] = cnt;
+ m.attacks[idx] = reference[i];
+ }
+ else if (m.attacks[idx] != reference[i])
+ break;
+ }
+ }
+ #endif
+ }
+}
+
+ #if defined(USE_COMPTIME_ATTACKS) && defined(USE_PEXT)
+constexpr auto RookTable = []() {
+ std::array result{};
+ Magic magics[64][2] = {};
+ init_magics(ROOK, result.data(), magics, false);
+ return result;
+}();
+constexpr auto BishopTable = []() {
+ std::array result{};
+ Magic magics[64][2] = {};
+ init_magics(BISHOP, result.data(), magics, false);
+ return result;
+}();
+ #else
+std::array RookTable;
+std::array BishopTable;
+ #endif
+}
+
+#endif
+
+void init() {
+
+#ifdef USE_HYPERBOLA_QUINT
+ init_magics(Magics);
+#else
+ init_magics(ROOK, const_cast(RookTable.data()), Magics, true);
+ init_magics(BISHOP, const_cast(BishopTable.data()), Magics, true);
+#endif
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ {
+ for (PieceType pt : {BISHOP, ROOK})
+ for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
+ {
+ if (PseudoAttacks[pt][s1] & s2)
+ {
+ LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
+ BetweenBB[s1][s2] =
+ (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
+ RayPassBB[s1][s2] =
+ attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
+ }
+ BetweenBB[s1][s2] |= s2;
+ }
+ }
+}
+
+const Magic& magic(Square s, PieceType pt) {
+ assert((pt == BISHOP || pt == ROOK) && is_ok(s));
+ return Magics[s][pt - BISHOP];
+}
+
+Bitboard line_bb(Square s1, Square s2) {
+ assert(is_ok(s1) && is_ok(s2));
+ return LineBB[s1][s2];
+}
+
+Bitboard between_bb(Square s1, Square s2) {
+ assert(is_ok(s1) && is_ok(s2));
+ return BetweenBB[s1][s2];
+}
+
+Bitboard ray_pass_bb(Square s1, Square s2) {
+ assert(is_ok(s1) && is_ok(s2));
+ return RayPassBB[s1][s2];
+}
+
+} // namespace Stockfish::Attacks
diff --git a/src/attacks.h b/src/attacks.h
new file mode 100644
index 000000000..f13eb6814
--- /dev/null
+++ b/src/attacks.h
@@ -0,0 +1,262 @@
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
+
+ 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 ATTACKS_H_INCLUDED
+#define ATTACKS_H_INCLUDED
+
+#include
+#include
+#include
+
+#include "types.h"
+#include "bitboard.h"
+
+#ifdef __aarch64__
+ #include
+ #define USE_HYPERBOLA_QUINT
+#elif defined(__loongarch__) && __loongarch_grlen == 64
+ #define USE_HYPERBOLA_QUINT
+#endif
+
+namespace Stockfish::Attacks {
+
+void init();
+
+#ifdef USE_HYPERBOLA_QUINT
+
+inline Bitboard reverse_bb(Bitboard bb) {
+ #ifdef __aarch64__
+ return __rbitll(bb);
+ #else // loongarch
+ Bitboard out;
+ asm("bitrev.d %0, %1" : "=r"(out) : "r"(bb));
+ return out;
+ #endif
+}
+
+// Hyperbola quintessence implementation for ARM, thanks to the availability of an
+// efficient bit reversal instruction.
+// See https://www.chessprogramming.org/Hyperbola_Quintessence
+struct Magic {
+ // For rooks: file attacks, rank attacks. For bishops: diagonal/antidiagonal
+ Bitboard mask1, mask2;
+ // Precomputed 2 * square_bb(sq), 2 * reverse(square_bb(sq))
+ Bitboard r, rr;
+
+ Bitboard hyperbola(Bitboard occupied, Bitboard mask) const {
+ Bitboard o = occupied & mask;
+ Bitboard fwd = o - r;
+ Bitboard rev = reverse_bb(o) - rr;
+ return (fwd ^ reverse_bb(rev)) & mask;
+ }
+
+ Bitboard attacks_bb(Bitboard occupied) const {
+ return hyperbola(occupied, mask1) | hyperbola(occupied, mask2);
+ }
+};
+#else
+// Magic holds all magic bitboards relevant data for a single square
+struct Magic {
+ Bitboard mask;
+ #ifdef USE_PEXT
+ uint16_t* attacks;
+ Bitboard pseudoAttacks;
+ #else
+ Bitboard* attacks;
+ Bitboard magic;
+ unsigned shift;
+ #endif
+
+ // Compute the attack's index using the 'magic bitboards' approach
+ unsigned index(Bitboard occupied) const {
+
+ #ifdef USE_PEXT
+ return unsigned(pext(occupied, mask));
+ #else
+ if (Is64Bit)
+ return unsigned(((occupied & mask) * magic) >> shift);
+
+ unsigned lo = unsigned(occupied) & unsigned(mask);
+ unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
+ return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
+ #endif
+ }
+
+ Bitboard attacks_bb(Bitboard occupied) const {
+ #ifdef USE_PEXT
+ return pdep(attacks[index(occupied)], pseudoAttacks);
+ #else
+ return attacks[index(occupied)];
+ #endif
+ }
+};
+#endif
+
+const Magic& magic(Square s, PieceType pt);
+Bitboard line_bb(Square s1, Square s2);
+Bitboard between_bb(Square s1, Square s2);
+Bitboard ray_pass_bb(Square s1, Square s2);
+
+// Returns the bitboard of target square for the given step
+// from the given square. If the step is off the board, returns empty bitboard.
+constexpr Bitboard safe_destination(Square s, int step) {
+ constexpr auto abs = [](int v) { return v < 0 ? -v : v; };
+ Square to = Square(s + step);
+ return is_ok(to) && abs(file_of(s) - file_of(to)) <= 2 ? square_bb(to) : Bitboard(0);
+}
+
+constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
+ Bitboard attacks = 0, dest = 0;
+ constexpr Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
+ constexpr Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
+
+ for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
+ {
+ Square s = sq;
+ while ((dest = safe_destination(s, d)))
+ {
+ attacks |= dest;
+ s += d;
+ if (occupied & dest)
+ {
+ break;
+ }
+ }
+ }
+
+ return attacks;
+}
+
+constexpr Bitboard knight_attack(Square sq) {
+ Bitboard b = {};
+ for (int step : {-17, -15, -10, -6, 6, 10, 15, 17})
+ b |= safe_destination(sq, step);
+ return b;
+}
+
+constexpr Bitboard king_attack(Square sq) {
+ Bitboard b = {};
+ for (int step : {-9, -8, -7, -1, 1, 7, 8, 9})
+ b |= safe_destination(sq, step);
+ return b;
+}
+
+constexpr Bitboard pseudo_attacks(PieceType pt, Square sq) {
+ switch (pt)
+ {
+ case PieceType::ROOK :
+ case PieceType::BISHOP :
+ return sliding_attack(pt, sq, 0);
+ case PieceType::QUEEN :
+ return sliding_attack(PieceType::ROOK, sq, 0) | sliding_attack(PieceType::BISHOP, sq, 0);
+ case PieceType::KNIGHT :
+ return knight_attack(sq);
+ case PieceType::KING :
+ return king_attack(sq);
+ default :
+ assert(false);
+ return 0;
+ }
+}
+
+inline constexpr auto PseudoAttacks = []() constexpr {
+ std::array, PIECE_TYPE_NB> attacks{};
+
+ for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
+ {
+ attacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1));
+ attacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1));
+
+ attacks[KING][s1] = pseudo_attacks(KING, s1);
+ attacks[KNIGHT][s1] = pseudo_attacks(KNIGHT, s1);
+ attacks[QUEEN][s1] = attacks[BISHOP][s1] = pseudo_attacks(BISHOP, s1);
+ attacks[QUEEN][s1] |= attacks[ROOK][s1] = pseudo_attacks(ROOK, s1);
+ }
+
+ 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.
+template
+inline Bitboard attacks_bb(Square s, Color c = COLOR_NB) {
+
+ assert((Pt != PAWN || c < COLOR_NB) && is_ok(s));
+ return Pt == PAWN ? PseudoAttacks[c][s] : PseudoAttacks[Pt][s];
+}
+
+// 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) {
+
+ assert(Pt != PAWN && is_ok(s));
+
+ switch (Pt)
+ {
+ case BISHOP :
+ case ROOK :
+ return magic(s, Pt).attacks_bb(occupied);
+ case QUEEN :
+ return attacks_bb(s, occupied) | attacks_bb(s, occupied);
+ default :
+ return PseudoAttacks[Pt][s];
+ }
+}
+
+// Returns the attacks by the given piece
+// assuming the board is occupied according to the passed Bitboard.
+// Sliding piece attacks do not continue passed an occupied square.
+inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
+
+ assert(pt != PAWN && is_ok(s));
+
+ switch (pt)
+ {
+ case BISHOP :
+ return attacks_bb(s, occupied);
+ case ROOK :
+ return attacks_bb(s, occupied);
+ case QUEEN :
+ return attacks_bb(s, occupied) | attacks_bb(s, occupied);
+ default :
+ return PseudoAttacks[pt][s];
+ }
+}
+
+inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
+ return type_of(pc) == PAWN ? PseudoAttacks[color_of(pc)][s]
+ : attacks_bb(type_of(pc), s, occupied);
+}
+
+} // namespace Stockfish::Attacks
+
+#endif // #ifndef ATTACKS_H_INCLUDED
diff --git a/src/bitboard.cpp b/src/bitboard.cpp
index cab666fc3..b9cc284e9 100644
--- a/src/bitboard.cpp
+++ b/src/bitboard.cpp
@@ -18,29 +18,13 @@
#include "bitboard.h"
-#include
#include
-#include
-
-#include "misc.h"
namespace Stockfish {
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
-Bitboard LineBB[SQUARE_NB][SQUARE_NB];
-Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
-Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
-
-alignas(64) Magic Magics[SQUARE_NB][2];
-
-#ifdef USE_PEXT
-using MagicMask = uint16_t;
-#else
-using MagicMask = Bitboard;
-#endif
-
// Returns an ASCII representation of a bitboard suitable
// to be printed to standard output. Useful for debugging.
std::string Bitboards::pretty(Bitboard b) {
@@ -62,178 +46,6 @@ std::string Bitboards::pretty(Bitboard b) {
return s;
}
-#ifdef USE_HYPERBOLA_QUINT
-static Bitboard line_mask(Square sq, Direction d1, Direction d2) {
- Bitboard mask = 0, dest;
- for (Direction d : {d1, d2})
- {
- Square s = sq;
- while ((dest = Bitboards::safe_destination(s, d)))
- {
- mask |= dest;
- s += d;
- }
- }
- return mask;
-}
-
-static void init_magics(Magic magics[][2]) {
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
- {
- Magic& rook = magics[s][ROOK - BISHOP];
- rook.mask1 = line_mask(s, NORTH, SOUTH);
- rook.mask2 = line_mask(s, EAST, WEST);
-
- Magic& bishop = magics[s][BISHOP - BISHOP];
- bishop.mask1 = line_mask(s, NORTH_EAST, SOUTH_WEST);
- bishop.mask2 = line_mask(s, NORTH_WEST, SOUTH_EAST);
-
- rook.r = bishop.r = square_bb(s) * 2;
- rook.rr = bishop.rr = square_bb(Square(63 - int(s))) * 2;
- }
-}
-
-#else
-
-namespace {
-[[maybe_unused]] constexpr Bitboard constexpr_pext(Bitboard b, Bitboard m) {
- Bitboard result = 0, bit = 0;
- while (m)
- {
- Bitboard last = m & -m;
- result |= bool(b & last) << bit++;
- m ^= last;
- }
- return result;
-}
-
- // Computes all rook and bishop attacks at startup or optionally, compile time. Magic
- // bitboards are used to look up attacks of sliding pieces. As a reference see
- // https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
- // the so called "fancy" approach.
- #ifdef USE_COMPTIME_ATTACKS
-constexpr
- #endif
- void
- init_magics(PieceType pt,
- MagicMask table[],
- Magic magics[][2],
- [[maybe_unused]] bool tableAlreadyInit) {
- #if !defined(USE_COMPTIME_ATTACKS)
- tableAlreadyInit = false;
- #endif
-
- #ifndef USE_PEXT
- // Optimal PRNG seeds to pick the correct magics in the shortest time
- int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
- {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
-
- Bitboard occupancy[4096];
- int epoch[4096] = {}, cnt = 0;
- Bitboard reference[4096] = {};
- #endif
- int size = 0;
-
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
- {
- // Board edges are not considered in the relevant occupancies
- Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
-
- // Given a square 's', the mask is the bitboard of sliding attacks from
- // 's' computed on an empty board. The index must be big enough to contain
- // all the attacks for each possible subset of the mask and so is 2 power
- // the number of 1s of the mask. Hence we deduce the size of the shift to
- // apply to the 64 or 32 bits word to get the index.
- Magic& m = magics[s][pt - BISHOP];
- Bitboard attacks = Bitboards::sliding_attack(pt, s, 0);
- m.mask = attacks & ~edges;
- #ifdef USE_PEXT
- m.pseudoAttacks = attacks;
- #else
- m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
- #endif
- // Set the offset for the attacks table of the square. We have individual
- // table sizes for each square with "Fancy Magic Bitboards".
- m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
- size = 0;
-
- // Use Carry-Rippler trick to enumerate all subsets of masks[s] and
- // store the corresponding sliding attack bitboard in reference[].
- Bitboard b = 0;
- [[maybe_unused]] Bitboard prevSliding = -1;
- do
- {
- #ifdef USE_PEXT
- if (!tableAlreadyInit)
- {
- Bitboard sliding = Bitboards::sliding_attack(pt, s, b);
- m.attacks[size] =
- sliding != prevSliding ? constexpr_pext(sliding, attacks) : m.attacks[size - 1];
- prevSliding = sliding;
- }
- #else
- occupancy[size] = b;
- reference[size] = Bitboards::sliding_attack(pt, s, b);
- #endif
-
- size++;
- b = (b - m.mask) & m.mask;
- } while (b);
-
- #ifndef USE_PEXT
- PRNG rng(seeds[Is64Bit][rank_of(s)]);
-
- // Find a magic for square 's' picking up an (almost) random number
- // until we find the one that passes the verification test.
- for (int i = 0; i < size;)
- {
- for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;)
- m.magic = rng.sparse_rand();
-
- // A good magic must map every possible occupancy to an index that
- // looks up the correct sliding attack in the attacks[s] database.
- // Note that we build up the database for square 's' as a side
- // effect of verifying the magic. Keep track of the attempt count
- // and save it in epoch[], little speed-up trick to avoid resetting
- // m.attacks[] after every failed attempt.
- for (++cnt, i = 0; i < size; ++i)
- {
- unsigned idx = m.index(occupancy[i]);
-
- if (epoch[idx] < cnt)
- {
- epoch[idx] = cnt;
- m.attacks[idx] = reference[i];
- }
- else if (m.attacks[idx] != reference[i])
- break;
- }
- }
- #endif
- }
-}
-
- #if defined(USE_COMPTIME_ATTACKS) && defined(USE_PEXT)
-constexpr auto RookTable = []() {
- std::array result{};
- Magic magics[64][2] = {};
- init_magics(ROOK, result.data(), magics, false);
- return result;
-}();
-constexpr auto BishopTable = []() {
- std::array result{};
- Magic magics[64][2] = {};
- init_magics(BISHOP, result.data(), magics, false);
- return result;
-}();
- #else
-std::array RookTable;
-std::array BishopTable;
- #endif
-}
-
-#endif
-
// Initializes various bitboard tables. It is called at
// startup and relies on global objects to be already zero-initialized.
void Bitboards::init() {
@@ -244,30 +56,6 @@ 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));
-
-#ifdef USE_HYPERBOLA_QUINT
- init_magics(Magics);
-#else
- init_magics(ROOK, const_cast(RookTable.data()), Magics, true);
- init_magics(BISHOP, const_cast(BishopTable.data()), Magics, true);
-#endif
-
- for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
- {
- for (PieceType pt : {BISHOP, ROOK})
- for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
- {
- if (PseudoAttacks[pt][s1] & s2)
- {
- LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
- BetweenBB[s1][s2] =
- (attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
- RayPassBB[s1][s2] =
- attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
- }
- BetweenBB[s1][s2] |= s2;
- }
- }
}
} // namespace Stockfish
diff --git a/src/bitboard.h b/src/bitboard.h
index eb86e1a65..1461380a2 100644
--- a/src/bitboard.h
+++ b/src/bitboard.h
@@ -26,18 +26,9 @@
#include
#include
#include
-#include
-#include
#include "types.h"
-#ifdef __aarch64__
- #include
- #define USE_HYPERBOLA_QUINT
-#elif defined(__loongarch__) && __loongarch_grlen == 64
- #define USE_HYPERBOLA_QUINT
-#endif
-
namespace Stockfish {
namespace Bitboards {
@@ -77,82 +68,6 @@ constexpr Bitboard Rank8BB = Rank1BB << (8 * 7);
extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
-extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
-extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
-extern Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
-
-#ifdef USE_HYPERBOLA_QUINT
-
-inline Bitboard reverse_bb(Bitboard bb) {
- #ifdef __aarch64__
- return __rbitll(bb);
- #else // loongarch
- Bitboard out;
- asm("bitrev.d %0, %1" : "=r"(out) : "r"(bb));
- return out;
- #endif
-}
-
-// Hyperbola quintessence implementation for ARM, thanks to the availability of an
-// efficient bit reversal instruction.
-// See https://www.chessprogramming.org/Hyperbola_Quintessence
-struct Magic {
- // For rooks: file attacks, rank attacks. For bishops: diagonal/antidiagonal
- Bitboard mask1, mask2;
- // Precomputed 2 * square_bb(sq), 2 * reverse(square_bb(sq))
- Bitboard r, rr;
-
- Bitboard hyperbola(Bitboard occupied, Bitboard mask) const {
- Bitboard o = occupied & mask;
- Bitboard fwd = o - r;
- Bitboard rev = reverse_bb(o) - rr;
- return (fwd ^ reverse_bb(rev)) & mask;
- }
-
- Bitboard attacks_bb(Bitboard occupied) const {
- return hyperbola(occupied, mask1) | hyperbola(occupied, mask2);
- }
-};
-#else
-// Magic holds all magic bitboards relevant data for a single square
-struct Magic {
- Bitboard mask;
- #ifdef USE_PEXT
- uint16_t* attacks;
- Bitboard pseudoAttacks;
- #else
- Bitboard* attacks;
- Bitboard magic;
- unsigned shift;
- #endif
-
- // Compute the attack's index using the 'magic bitboards' approach
- unsigned index(Bitboard occupied) const {
-
- #ifdef USE_PEXT
- return unsigned(pext(occupied, mask));
- #else
- if (Is64Bit)
- return unsigned(((occupied & mask) * magic) >> shift);
-
- unsigned lo = unsigned(occupied) & unsigned(mask);
- unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
- return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
- #endif
- }
-
- Bitboard attacks_bb(Bitboard occupied) const {
- #ifdef USE_PEXT
- return pdep(attacks[index(occupied)], pseudoAttacks);
- #else
- return attacks[index(occupied)];
- #endif
- }
-};
-#endif
-
-extern Magic Magics[SQUARE_NB][2];
-
constexpr Bitboard square_bb(Square s) {
assert(is_ok(s));
return 1ULL << s;
@@ -218,30 +133,6 @@ 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
-// are not on a same file/rank/diagonal, the function returns 0. For instance,
-// line_bb(SQ_C4, SQ_F7) will return a bitboard with the A2-G8 diagonal.
-inline Bitboard line_bb(Square s1, Square s2) {
-
- assert(is_ok(s1) && is_ok(s2));
- return LineBB[s1][s2];
-}
-
-
-// Returns a bitboard representing the squares in the semi-open
-// segment between the squares s1 and s2 (excluding s1 but including s2). If the
-// given squares are not on a same file/rank/diagonal, it returns s2. For instance,
-// between_bb(SQ_C4, SQ_F7) will return a bitboard with squares D5, E6 and F7, but
-// between_bb(SQ_E6, SQ_F8) will return a bitboard with the square F8. This trick
-// allows to generate non-king evasion moves faster: the defending piece must either
-// interpose itself to cover the check or capture the checking piece.
-inline Bitboard between_bb(Square s1, Square s2) {
-
- assert(is_ok(s1) && is_ok(s2));
- return BetweenBB[s1][s2];
-}
-
// distance() functions return the distance between x and y, defined as the
// number of steps for a king in x to reach y.
@@ -389,156 +280,6 @@ inline Square pop_lsb(Bitboard& b) {
return s;
}
-namespace Bitboards {
-// Returns the bitboard of target square for the given step
-// from the given square. If the step is off the board, returns empty bitboard.
-constexpr Bitboard safe_destination(Square s, int step) {
- constexpr auto abs = [](int v) { return v < 0 ? -v : v; };
- Square to = Square(s + step);
- return is_ok(to) && abs(file_of(s) - file_of(to)) <= 2 ? square_bb(to) : Bitboard(0);
-}
-
-constexpr Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) {
- Bitboard attacks = 0, dest = 0;
- constexpr Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST};
- constexpr Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST};
-
- for (Direction d : (pt == ROOK ? RookDirections : BishopDirections))
- {
- Square s = sq;
- while ((dest = safe_destination(s, d)))
- {
- attacks |= dest;
- s += d;
- if (occupied & dest)
- {
- break;
- }
- }
- }
-
- return attacks;
-}
-
-constexpr Bitboard knight_attack(Square sq) {
- Bitboard b = {};
- for (int step : {-17, -15, -10, -6, 6, 10, 15, 17})
- b |= safe_destination(sq, step);
- return b;
-}
-
-constexpr Bitboard king_attack(Square sq) {
- Bitboard b = {};
- for (int step : {-9, -8, -7, -1, 1, 7, 8, 9})
- b |= safe_destination(sq, step);
- return b;
-}
-
-constexpr Bitboard pseudo_attacks(PieceType pt, Square sq) {
- switch (pt)
- {
- case PieceType::ROOK :
- case PieceType::BISHOP :
- return sliding_attack(pt, sq, 0);
- case PieceType::QUEEN :
- return sliding_attack(PieceType::ROOK, sq, 0) | sliding_attack(PieceType::BISHOP, sq, 0);
- case PieceType::KNIGHT :
- return knight_attack(sq);
- case PieceType::KING :
- return king_attack(sq);
- default :
- assert(false);
- return 0;
- }
-}
-
-}
-
-inline constexpr auto PseudoAttacks = []() constexpr {
- std::array, PIECE_TYPE_NB> attacks{};
-
- for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
- {
- attacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1));
- attacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1));
-
- attacks[KING][s1] = Bitboards::pseudo_attacks(KING, s1);
- attacks[KNIGHT][s1] = Bitboards::pseudo_attacks(KNIGHT, s1);
- attacks[QUEEN][s1] = attacks[BISHOP][s1] = Bitboards::pseudo_attacks(BISHOP, s1);
- attacks[QUEEN][s1] |= attacks[ROOK][s1] = Bitboards::pseudo_attacks(ROOK, s1);
- }
-
- 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.
-template
-inline Bitboard attacks_bb(Square s, Color c = COLOR_NB) {
-
- assert((Pt != PAWN || c < COLOR_NB) && is_ok(s));
- return Pt == PAWN ? PseudoAttacks[c][s] : PseudoAttacks[Pt][s];
-}
-
-
-// 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) {
-
- assert(Pt != PAWN && is_ok(s));
-
- switch (Pt)
- {
- case BISHOP :
- case ROOK :
- return Magics[s][Pt - BISHOP].attacks_bb(occupied);
- case QUEEN :
- return attacks_bb(s, occupied) | attacks_bb(s, occupied);
- default :
- return PseudoAttacks[Pt][s];
- }
-}
-
-// Returns the attacks by the given piece
-// assuming the board is occupied according to the passed Bitboard.
-// Sliding piece attacks do not continue passed an occupied square.
-inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
-
- assert(pt != PAWN && is_ok(s));
-
- switch (pt)
- {
- case BISHOP :
- return attacks_bb(s, occupied);
- case ROOK :
- return attacks_bb(s, occupied);
- case QUEEN :
- return attacks_bb(s, occupied) | attacks_bb(s, occupied);
- default :
- return PseudoAttacks[pt][s];
- }
-}
-
-inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) {
- return type_of(pc) == PAWN ? PseudoAttacks[color_of(pc)][s]
- : attacks_bb(type_of(pc), s, occupied);
-}
-
} // namespace Stockfish
#endif // #ifndef BITBOARD_H_INCLUDED
diff --git a/src/main.cpp b/src/main.cpp
index 6a5f6fe68..c386c4189 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,6 +19,7 @@
#include
#include
+#include "attacks.h"
#include "bitboard.h"
#include "misc.h"
#include "position.h"
@@ -39,6 +40,7 @@ int main(int argc, char* argv[]) {
std::cout << engine_info() << std::endl;
Bitboards::init();
+ Attacks::init();
Position::init();
auto uci = std::make_unique(argc, argv);
diff --git a/src/movegen.cpp b/src/movegen.cpp
index 86edf6ce2..4f048fee2 100644
--- a/src/movegen.cpp
+++ b/src/movegen.cpp
@@ -21,6 +21,7 @@
#include
#include
+#include "attacks.h"
#include "bitboard.h"
#include "position.h"
@@ -70,7 +71,7 @@ alignas(64) constexpr auto SliderMoves = []() {
{
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
- Bitboard bb = PseudoAttacks[pt][s];
+ Bitboard bb = Attacks::PseudoAttacks[pt][s];
int i = 0;
while (bb)
{
@@ -89,7 +90,7 @@ alignas(64) constexpr auto KnightKingMoves = []() {
{
for (Square s = SQ_A1; s <= SQ_H8; ++s)
{
- Bitboard bb = PseudoAttacks[pt][s];
+ Bitboard bb = Attacks::PseudoAttacks[pt][s];
int i = 0;
while (bb)
{
@@ -110,7 +111,7 @@ splat_precomputed_moves(Move* moveList, Square from, Bitboard occupied, Bitboard
uint32_t mask;
if constexpr (Pt == BISHOP || Pt == ROOK)
{
- const Magic& magic = Magics[from][Pt - BISHOP];
+ const Attacks::Magic& magic = Attacks::magic(from, Pt);
mask = magic.attacks[magic.index(occupied)];
mask &= pext(target, magic.pseudoAttacks);
@@ -122,7 +123,7 @@ splat_precomputed_moves(Move* moveList, Square from, Bitboard occupied, Bitboard
}
else
{
- mask = pext(target, PseudoAttacks[Pt][from]);
+ mask = pext(target, Attacks::PseudoAttacks[Pt][from]);
__m128i moves = *reinterpret_cast(KnightKingMoves[Pt == KING][from].data());
_mm_storeu_si128(reinterpret_cast<__m128i*>(moveList),
@@ -240,7 +241,7 @@ Move* generate_pawn_moves(const Position& pos, Move* moveList, Bitboard target)
if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
return moveList;
- b1 = pawnsNotOn7 & attacks_bb(pos.ep_square(), Them);
+ b1 = pawnsNotOn7 & Attacks::attacks_bb(pos.ep_square(), Them);
assert(b1);
@@ -270,7 +271,7 @@ Move* generate_moves(const Position& pos, Move* moveList, Bitboard target) {
continue;
}
#endif
- Bitboard b = attacks_bb(from, pos.pieces()) & target;
+ Bitboard b = Attacks::attacks_bb(from, pos.pieces()) & target;
moveList = splat_moves(moveList, from, b);
}
@@ -290,7 +291,7 @@ Move* generate_all(const Position& pos, Move* moveList) {
// Skip generating non-king moves when in double check
if (Type != EVASIONS || !more_than_one(pos.checkers()))
{
- target = Type == EVASIONS ? between_bb(ksq, lsb(pos.checkers()))
+ target = Type == EVASIONS ? Attacks::between_bb(ksq, lsb(pos.checkers()))
: Type == NON_EVASIONS ? ~pos.pieces(Us)
: Type == CAPTURES ? pos.pieces(~Us)
: ~pos.pieces(); // QUIETS
@@ -307,7 +308,7 @@ Move* generate_all(const Position& pos, Move* moveList) {
#ifdef USE_AVX512ICL
moveList = splat_precomputed_moves(moveList, ksq, 0ULL, b);
#else
- moveList = splat_moves(moveList, ksq, attacks_bb(ksq) & b);
+ moveList = splat_moves(moveList, ksq, Attacks::attacks_bb(ksq) & b);
#endif
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
diff --git a/src/nnue/features/full_threats.cpp b/src/nnue/features/full_threats.cpp
index 6811cea65..83d0fcc11 100644
--- a/src/nnue/features/full_threats.cpp
+++ b/src/nnue/features/full_threats.cpp
@@ -26,6 +26,7 @@
#include
#include
+#include "../../attacks.h"
#include "../../bitboard.h"
#include "../../misc.h"
#include "../../position.h"
@@ -51,7 +52,7 @@ constexpr auto make_piece_indices_type() {
for (Square from = SQ_A1; from <= SQ_H8; ++from)
{
- Bitboard attacks = PseudoAttacks[PT][from];
+ Bitboard attacks = Attacks::PseudoAttacks[PT][from];
for (Square to = SQ_A1; to <= SQ_H8; ++to)
{
@@ -72,7 +73,7 @@ constexpr auto make_piece_indices_piece() {
for (Square from = SQ_A1; from <= SQ_H8; ++from)
{
- Bitboard attacks = PawnPushOrAttacks[C][from];
+ Bitboard attacks = Attacks::PawnPushOrAttacks[C][from];
for (Square to = SQ_A1; to <= SQ_H8; ++to)
{
@@ -129,14 +130,14 @@ constexpr auto init_threat_offsets() {
if (type_of(piece) != PAWN)
{
- Bitboard attacks = PseudoAttacks[type_of(piece)][from];
+ Bitboard attacks = Attacks::PseudoAttacks[type_of(piece)][from];
cumulativePieceOffset += constexpr_popcount(attacks);
}
else if (from >= SQ_A2 && from <= SQ_H7)
{
- Bitboard attacks =
- (pieceIdx < 8) ? PawnPushOrAttacks[WHITE][from] : PawnPushOrAttacks[BLACK][from];
+ Bitboard attacks = (pieceIdx < 8) ? Attacks::PawnPushOrAttacks[WHITE][from]
+ : Attacks::PawnPushOrAttacks[BLACK][from];
cumulativePieceOffset += constexpr_popcount(attacks);
}
}
@@ -254,7 +255,7 @@ void FullThreats::append_active_indices(Color perspective, const Position& pos,
while (bb)
{
Square from = pop_lsb(bb);
- Bitboard attacks = attacks_bb(pt, from, occupied) & occupied;
+ Bitboard attacks = Attacks::attacks_bb(pt, from, occupied) & occupied;
while (attacks)
{
Square to = pop_lsb(attacks);
diff --git a/src/position.cpp b/src/position.cpp
index 71a56cb1b..4c0b1dbf5 100644
--- a/src/position.cpp
+++ b/src/position.cpp
@@ -43,6 +43,8 @@ using std::string;
namespace Stockfish {
+using namespace Attacks;
+
namespace Zobrist {
Key psq[PIECE_NB][SQUARE_NB];
@@ -1216,11 +1218,11 @@ void Position::update_piece_threats(Piece pc,
Square sliderSq = pop_lsb(sliders);
Piece slider = piece_on(sliderSq);
- const Bitboard ray = RayPassBB[sliderSq][s];
+ const Bitboard ray = ray_pass_bb(sliderSq, s);
const Bitboard discovered = ray & (rAttacks | bAttacks) & occupiedNoK;
assert(!more_than_one(discovered));
- if (discovered && (RayPassBB[sliderSq][s] & noRaysContaining) != noRaysContaining)
+ if (discovered && (ray_pass_bb(sliderSq, s) & noRaysContaining) != noRaysContaining)
{
const Square threatenedSq = lsb(discovered);
const Piece threatenedPc = piece_on(threatenedSq);
diff --git a/src/position.h b/src/position.h
index 8027988a0..25b29e8b9 100644
--- a/src/position.h
+++ b/src/position.h
@@ -29,6 +29,7 @@
#include
#include
+#include "attacks.h"
#include "bitboard.h"
#include "types.h"
@@ -301,7 +302,7 @@ inline Bitboard Position::attacks_by(Color c) const {
Bitboard threats = 0;
Bitboard attackers = pieces(c, Pt);
while (attackers)
- threats |= attacks_bb(pop_lsb(attackers), pieces());
+ threats |= Attacks::attacks_bb(pop_lsb(attackers), pieces());
return threats;
}
}
diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp
index b75b6046a..b3a7b3a6c 100644
--- a/src/syzygy/tbprobe.cpp
+++ b/src/syzygy/tbprobe.cpp
@@ -38,6 +38,7 @@
#include
#include
+#include "../attacks.h"
#include "../bitboard.h"
#include "../misc.h"
#include "../movegen.h"
@@ -1406,7 +1407,7 @@ void Tablebases::init(const std::string& paths) {
if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0
{
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
- if ((PseudoAttacks[KING][s1] | s1) & s2)
+ if ((Attacks::PseudoAttacks[KING][s1] | s1) & s2)
continue; // Illegal position
else if (!off_A1H8(s1) && off_A1H8(s2) > 0)