diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 6c97d7863..350e56c92 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -32,7 +32,6 @@ 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]; -Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; alignas(64) Magic Magics[SQUARE_NB][2]; @@ -42,13 +41,6 @@ Bitboard RookTable[0x19000]; // To store rook attacks Bitboard BishopTable[0x1480]; // To store bishop attacks void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]); - -// Returns the bitboard of target square for the given step -// from the given square. If the step is off the board, returns empty bitboard. -Bitboard safe_destination(Square s, int step) { - Square to = Square(s + step); - return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0); -} } // Returns an ASCII representation of a bitboard suitable @@ -86,18 +78,6 @@ void Bitboards::init() { for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) { - PseudoAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); - PseudoAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); - - for (int step : {-9, -8, -7, -1, 1, 7, 8, 9}) - PseudoAttacks[KING][s1] |= safe_destination(s1, step); - - for (int step : {-17, -15, -10, -6, 6, 10, 15, 17}) - PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); - - PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); - PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb(s1, 0); - for (PieceType pt : {BISHOP, ROOK}) for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) { @@ -115,30 +95,6 @@ void Bitboards::init() { } namespace { - -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 (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) - { - Square s = sq; - while (safe_destination(s, d)) - { - attacks |= (s += d); - if (occupied & s) - { - break; - } - } - } - - return attacks; -} - - // Computes all rook and bishop attacks at startup. 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 @@ -167,7 +123,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) { // 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]; - m.mask = sliding_attack(pt, s, 0) & ~edges; + m.mask = Bitboards::sliding_attack(pt, s, 0) & ~edges; #ifndef USE_PEXT m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); #endif @@ -184,7 +140,7 @@ void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) { #ifndef USE_PEXT occupancy[size] = b; #endif - reference[size] = sliding_attack(pt, s, b); + reference[size] = Bitboards::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 1cd717b8f..1da11d45c 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include "types.h" @@ -62,8 +64,6 @@ 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]; -extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; - // Magic holds all magic bitboards relevant data for a single square struct Magic { @@ -203,69 +203,12 @@ inline int distance(Square x, Square y) { inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); } -// 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) { - if (type_of(pc) == PAWN) - return PseudoAttacks[color_of(pc)][s]; - - return PseudoAttacks[type_of(pc)][s]; -} - - -inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { - if (type_of(pc) == PAWN) - return PseudoAttacks[color_of(pc)][s]; - - return attacks_bb(type_of(pc), s, occupied); +constexpr int constexpr_popcount(Bitboard b) { + b = b - ((b >> 1) & 0x5555555555555555ULL); + b = (b & 0x3333333333333333ULL) + ((b >> 2) & 0x3333333333333333ULL); + b = (b + (b >> 4)) & 0x0F0F0F0F0F0F0F0FULL; + return static_cast((b * 0x0101010101010101ULL) >> 56); } // Counts the number of non-zero bits in a bitboard. @@ -373,6 +316,153 @@ 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; + 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)) + { + Square s = sq; + while (safe_destination(s, d)) + { + attacks |= (s += d); + if (occupied & s) + { + 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; +}(); + + +// 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) { + if (type_of(pc) == PAWN) + return PseudoAttacks[color_of(pc)][s]; + + return PseudoAttacks[type_of(pc)][s]; +} + + +inline Bitboard attacks_bb(Piece pc, Square s, Bitboard occupied) { + if (type_of(pc) == PAWN) + return PseudoAttacks[color_of(pc)][s]; + + return 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 6ab3507f3..107b5e43d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,7 +21,6 @@ #include "bitboard.h" #include "misc.h" -#include "nnue/features/full_threats.h" #include "position.h" #include "tune.h" #include "uci.h" @@ -33,7 +32,6 @@ int main(int argc, char* argv[]) { Bitboards::init(); Position::init(); - Eval::NNUE::Features::init_threat_offsets(); auto uci = std::make_unique(argc, argv); diff --git a/src/nnue/features/full_threats.cpp b/src/nnue/features/full_threats.cpp index 645bb7090..63b7f8e13 100644 --- a/src/nnue/features/full_threats.cpp +++ b/src/nnue/features/full_threats.cpp @@ -21,7 +21,9 @@ #include "full_threats.h" #include +#include #include +#include #include "../../bitboard.h" #include "../../misc.h" @@ -31,26 +33,28 @@ namespace Stockfish::Eval::NNUE::Features { -// Lookup array for indexing threats -IndexType offsets[PIECE_NB][SQUARE_NB]; - struct HelperOffsets { int cumulativePieceOffset, cumulativeOffset; }; -std::array helper_offsets; // Information on a particular pair of pieces and whether they should be excluded struct PiecePairData { // Layout: bits 8..31 are the index contribution of this piece pair, bits 0 and 1 are exclusion info uint32_t data; - PiecePairData() {} - PiecePairData(bool excluded_pair, bool semi_excluded_pair, IndexType feature_index_base) { - data = - excluded_pair << 1 | (semi_excluded_pair && !excluded_pair) | feature_index_base << 8; - } + + constexpr PiecePairData() : + data(0) {} + + constexpr PiecePairData(bool excluded_pair, + bool semi_excluded_pair, + IndexType feature_index_base) : + data((uint32_t(excluded_pair) << 1) | (uint32_t(semi_excluded_pair && !excluded_pair)) + | (uint32_t(feature_index_base) << 8)) {} + // lsb: excluded if from < to; 2nd lsb: always excluded - uint8_t excluded_pair_info() const { return (uint8_t) data; } - IndexType feature_index_base() const { return data >> 8; } + constexpr uint8_t excluded_pair_info() const { return static_cast(data); } + + constexpr IndexType feature_index_base() const { return static_cast(data >> 8); } }; constexpr std::array AllPieces = { @@ -58,12 +62,119 @@ constexpr std::array AllPieces = { B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING, }; -// The final index is calculated from summing data found in these two LUTs, as well -// as offsets[attacker][from] -PiecePairData index_lut1[PIECE_NB][PIECE_NB]; // [attacker][attacked] -uint8_t index_lut2[PIECE_NB][SQUARE_NB][SQUARE_NB]; // [attacker][from][to] +template +constexpr auto make_piece_indices_type() { + static_assert(PT != PieceType::PAWN); + + std::array, SQUARE_NB> out{}; + + for (int from = 0; from < SQUARE_NB; ++from) + { + Bitboard attacks = PseudoAttacks[PT][Square(from)]; + + for (int to = 0; to < SQUARE_NB; ++to) + { + out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks); + } + } + + return out; +} + +template +constexpr auto make_piece_indices_piece() { + static_assert(type_of(P) == PieceType::PAWN); + + std::array, SQUARE_NB> out{}; + + constexpr Color C = color_of(P); + + for (int from = 0; from < SQUARE_NB; ++from) + { + Bitboard attacks = PseudoAttacks[C][from]; + + for (int to = 0; to < SQUARE_NB; ++to) + { + out[from][to] = constexpr_popcount(((1ULL << to) - 1) & attacks); + } + } + + return out; +} + +constexpr auto index_lut2_array() { + constexpr auto KNIGHT_ATTACKS = make_piece_indices_type(); + constexpr auto BISHOP_ATTACKS = make_piece_indices_type(); + constexpr auto ROOK_ATTACKS = make_piece_indices_type(); + constexpr auto QUEEN_ATTACKS = make_piece_indices_type(); + constexpr auto KING_ATTACKS = make_piece_indices_type(); + + std::array, SQUARE_NB>, PIECE_NB> indices{}; + + indices[W_PAWN] = make_piece_indices_piece(); + indices[B_PAWN] = make_piece_indices_piece(); + + indices[W_KNIGHT] = KNIGHT_ATTACKS; + indices[B_KNIGHT] = KNIGHT_ATTACKS; + + indices[W_BISHOP] = BISHOP_ATTACKS; + indices[B_BISHOP] = BISHOP_ATTACKS; + + indices[W_ROOK] = ROOK_ATTACKS; + indices[B_ROOK] = ROOK_ATTACKS; + + indices[W_QUEEN] = QUEEN_ATTACKS; + indices[B_QUEEN] = QUEEN_ATTACKS; + + indices[W_KING] = KING_ATTACKS; + indices[B_KING] = KING_ATTACKS; + + return indices; +} + +constexpr auto init_threat_offsets() { + std::array indices{}; + std::array, PIECE_NB> offsets{}; + + int cumulativeOffset = 0; + for (Piece piece : AllPieces) + { + int pieceIdx = piece; + int cumulativePieceOffset = 0; + + for (Square from = SQ_A1; from <= SQ_H8; ++from) + { + offsets[pieceIdx][from] = cumulativePieceOffset; + + if (type_of(piece) != PAWN) + { + Bitboard attacks = PseudoAttacks[type_of(piece)][from]; + cumulativePieceOffset += constexpr_popcount(attacks); + } + + else if (from >= SQ_A2 && from <= SQ_H7) + { + Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb(square_bb(from)) + : pawn_attacks_bb(square_bb(from)); + cumulativePieceOffset += constexpr_popcount(attacks); + } + } + + indices[pieceIdx] = {cumulativePieceOffset, cumulativeOffset}; + + cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset; + } + + return std::pair{indices, offsets}; +} + +constexpr auto helper_offsets = init_threat_offsets().first; +// Lookup array for indexing threats +constexpr auto offsets = init_threat_offsets().second; + +constexpr auto init_index_luts() { + std::array, PIECE_NB> indices{}; -static void init_index_luts() { for (Piece attacker : AllPieces) { for (Piece attacked : AllPieces) @@ -78,56 +189,21 @@ static void init_index_luts() { + (color_of(attacked) * (numValidTargets[attacker] / 2) + map) * helper_offsets[attacker].cumulativePieceOffset; - bool excluded = map < 0; - index_lut1[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature); + bool excluded = map < 0; + indices[attacker][attacked] = PiecePairData(excluded, semi_excluded, feature); } } - for (Piece attacker : AllPieces) - { - for (int from = 0; from < SQUARE_NB; ++from) - { - for (int to = 0; to < SQUARE_NB; ++to) - { - Bitboard attacks = attacks_bb(attacker, Square(from)); - index_lut2[attacker][from][to] = popcount((square_bb(Square(to)) - 1) & attacks); - } - } - } + return indices; } -void init_threat_offsets() { - int cumulativeOffset = 0; - for (Piece piece : AllPieces) - { - int pieceIdx = piece; - int cumulativePieceOffset = 0; +// The final index is calculated from summing data found in these two LUTs, as well +// as offsets[attacker][from] - for (Square from = SQ_A1; from <= SQ_H8; ++from) - { - offsets[pieceIdx][from] = cumulativePieceOffset; - - if (type_of(piece) != PAWN) - { - Bitboard attacks = attacks_bb(piece, from, 0ULL); - cumulativePieceOffset += popcount(attacks); - } - - else if (from >= SQ_A2 && from <= SQ_H7) - { - Bitboard attacks = (pieceIdx < 8) ? pawn_attacks_bb(square_bb(from)) - : pawn_attacks_bb(square_bb(from)); - cumulativePieceOffset += popcount(attacks); - } - } - - helper_offsets[pieceIdx] = {cumulativePieceOffset, cumulativeOffset}; - - cumulativeOffset += numValidTargets[pieceIdx] * cumulativePieceOffset; - } - - init_index_luts(); -} +// [attacker][attacked] +constexpr auto index_lut1 = init_index_luts(); +// [attacker][from][to] +constexpr auto index_lut2 = index_lut2_array(); // Index of a feature for a given king position and another piece on some square inline sf_always_inline IndexType FullThreats::make_index( diff --git a/src/nnue/features/full_threats.h b/src/nnue/features/full_threats.h index 177e9fabe..e4fa3331b 100644 --- a/src/nnue/features/full_threats.h +++ b/src/nnue/features/full_threats.h @@ -32,7 +32,6 @@ namespace Stockfish::Eval::NNUE::Features { static constexpr int numValidTargets[PIECE_NB] = {0, 6, 12, 10, 10, 12, 8, 0, 0, 6, 12, 10, 10, 12, 8, 0}; -void init_threat_offsets(); class FullThreats { public: diff --git a/src/syzygy/tbprobe.cpp b/src/syzygy/tbprobe.cpp index e3f7c0a18..c371259d6 100644 --- a/src/syzygy/tbprobe.cpp +++ b/src/syzygy/tbprobe.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include "../bitboard.h" #include "../misc.h"