X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fbitboard.cpp;h=6b84b51e036bea5c1a62fbff17ff3f4530eb71ed;hp=5a4c8b6b82fd6fe5b3330fbbd1d3160ad9352672;hb=HEAD;hpb=6fb0a1bc4050dd9b15e9c163c46c60f25c48137d diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 5a4c8b6b..a8a10cbb 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -1,7 +1,6 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad + Copyright (C) 2004-2023 The Stockfish developers (see AUTHORS file) Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,247 +16,136 @@ along with this program. If not, see . */ -#include -#include // For memset - #include "bitboard.h" -#include "bitcount.h" -#include "rkiss.h" - -Bitboard RMasks[SQUARE_NB]; -Bitboard RMagics[SQUARE_NB]; -Bitboard* RAttacks[SQUARE_NB]; -unsigned RShifts[SQUARE_NB]; - -Bitboard BMasks[SQUARE_NB]; -Bitboard BMagics[SQUARE_NB]; -Bitboard* BAttacks[SQUARE_NB]; -unsigned BShifts[SQUARE_NB]; - -Bitboard SquareBB[SQUARE_NB]; -Bitboard FileBB[FILE_NB]; -Bitboard RankBB[RANK_NB]; -Bitboard AdjacentFilesBB[FILE_NB]; -Bitboard InFrontBB[COLOR_NB][RANK_NB]; -Bitboard StepAttacksBB[PIECE_NB][SQUARE_NB]; -Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; -Bitboard LineBB[SQUARE_NB][SQUARE_NB]; -Bitboard DistanceRingsBB[SQUARE_NB][8]; -Bitboard ForwardBB[COLOR_NB][SQUARE_NB]; -Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB]; -Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB]; -Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; - -int SquareDistance[SQUARE_NB][SQUARE_NB]; -namespace { - - // De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan - const uint64_t DeBruijn_64 = 0x3F79D71B4CB0A89ULL; - const uint32_t DeBruijn_32 = 0x783A9B23; - - int MS1BTable[256]; - Square BSFTable[SQUARE_NB]; - Bitboard RTable[0x19000]; // Storage space for rook attacks - Bitboard BTable[0x1480]; // Storage space for bishop attacks +#include +#include +#include - typedef unsigned (Fn)(Square, Bitboard); +#include "misc.h" - void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], - Bitboard masks[], unsigned shifts[], Square deltas[], Fn index); +namespace Stockfish { - FORCE_INLINE unsigned bsf_index(Bitboard b) { +uint8_t PopCnt16[1 << 16]; +uint8_t SquareDistance[SQUARE_NB][SQUARE_NB]; - // Matt Taylor's folding for 32 bit systems, extended to 64 bits by Kim Walisch - b ^= (b - 1); - return Is64Bit ? (b * DeBruijn_64) >> 58 - : ((unsigned(b) ^ unsigned(b >> 32)) * DeBruijn_32) >> 26; - } -} +Bitboard LineBB[SQUARE_NB][SQUARE_NB]; +Bitboard BetweenBB[SQUARE_NB][SQUARE_NB]; +Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB]; +Bitboard PawnAttacks[COLOR_NB][SQUARE_NB]; -/// lsb()/msb() finds the least/most significant bit in a non-zero bitboard. -/// pop_lsb() finds and clears the least significant bit in a non-zero bitboard. +Magic RookMagics[SQUARE_NB]; +Magic BishopMagics[SQUARE_NB]; -#ifndef USE_BSFQ +namespace { -Square lsb(Bitboard b) { return BSFTable[bsf_index(b)]; } +Bitboard RookTable[0x19000]; // To store rook attacks +Bitboard BishopTable[0x1480]; // To store bishop attacks -Square pop_lsb(Bitboard* b) { +void init_magics(PieceType pt, Bitboard table[], Magic magics[]); - Bitboard bb = *b; - *b = bb & (bb - 1); - return BSFTable[bsf_index(bb)]; } -Square msb(Bitboard b) { +// 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); +} - unsigned b32; - int result = 0; - if (b > 0xFFFFFFFF) - { - b >>= 32; - result = 32; - } +// Returns an ASCII representation of a bitboard suitable +// to be printed to standard output. Useful for debugging. +std::string Bitboards::pretty(Bitboard b) { - b32 = unsigned(b); + std::string s = "+---+---+---+---+---+---+---+---+\n"; - if (b32 > 0xFFFF) - { - b32 >>= 16; - result += 16; - } + for (Rank r = RANK_8; r >= RANK_1; --r) + { + for (File f = FILE_A; f <= FILE_H; ++f) + s += b & make_square(f, r) ? "| X " : "| "; - if (b32 > 0xFF) - { - b32 >>= 8; - result += 8; - } + s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n"; + } + s += " a b c d e f g h\n"; - return Square(result + MS1BTable[b32]); + return s; } -#endif // ifndef USE_BSFQ +// Initializes various bitboard tables. It is called at +// startup and relies on global objects to be already zero-initialized. +void Bitboards::init() { -/// Bitboards::pretty() returns an ASCII representation of a bitboard to be -/// printed to standard output. This is sometimes useful for debugging. - -const std::string Bitboards::pretty(Bitboard b) { - - std::string s = "+---+---+---+---+---+---+---+---+\n"; + for (unsigned i = 0; i < (1 << 16); ++i) + PopCnt16[i] = uint8_t(std::bitset<16>(i).count()); - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - s.append(b & make_square(f, r) ? "| X " : "| "); + 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)); - s.append("|\n+---+---+---+---+---+---+---+---+\n"); - } + init_magics(ROOK, RookTable, RookMagics); + init_magics(BISHOP, BishopTable, BishopMagics); - return s; -} + for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) + { + PawnAttacks[WHITE][s1] = pawn_attacks_bb(square_bb(s1)); + PawnAttacks[BLACK][s1] = pawn_attacks_bb(square_bb(s1)); + for (int step : {-9, -8, -7, -1, 1, 7, 8, 9}) + PseudoAttacks[KING][s1] |= safe_destination(s1, step); -/// Bitboards::init() initializes various bitboard tables. It is called at -/// startup and relies on global objects to be already zero-initialized. + for (int step : {-17, -15, -10, -6, 6, 10, 15, 17}) + PseudoAttacks[KNIGHT][s1] |= safe_destination(s1, step); -void Bitboards::init() { + PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); + PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ROOK][s1] = attacks_bb(s1, 0); - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - SquareBB[s] = 1ULL << s; - BSFTable[bsf_index(SquareBB[s])] = s; - } - - for (Bitboard b = 1; b < 256; ++b) - MS1BTable[b] = more_than_one(b) ? MS1BTable[b - 1] : lsb(b); - - for (File f = FILE_A; f <= FILE_H; ++f) - FileBB[f] = f > FILE_A ? FileBB[f - 1] << 1 : FileABB; - - for (Rank r = RANK_1; r <= RANK_8; ++r) - RankBB[r] = r > RANK_1 ? RankBB[r - 1] << 8 : Rank1BB; - - for (File f = FILE_A; f <= FILE_H; ++f) - AdjacentFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0); - - for (Rank r = RANK_1; r < RANK_8; ++r) - InFrontBB[WHITE][r] = ~(InFrontBB[BLACK][r + 1] = InFrontBB[BLACK][r] | RankBB[r]); - - for (Color c = WHITE; c <= BLACK; ++c) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - ForwardBB[c][s] = InFrontBB[c][rank_of(s)] & FileBB[file_of(s)]; - PawnAttackSpan[c][s] = InFrontBB[c][rank_of(s)] & AdjacentFilesBB[file_of(s)]; - PassedPawnMask[c][s] = ForwardBB[c][s] | PawnAttackSpan[c][s]; - } - - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - if (s1 != s2) - { - SquareDistance[s1][s2] = std::max(distance(s1, s2), distance(s1, s2)); - DistanceRingsBB[s1][SquareDistance[s1][s2] - 1] |= s2; - } - - int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 }, - {}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } }; - - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - for (int i = 0; steps[pt][i]; ++i) - { - Square to = s + Square(c == WHITE ? steps[pt][i] : -steps[pt][i]); - - if (is_ok(to) && distance(s, to) < 3) - StepAttacksBB[make_piece(c, pt)][s] |= to; - } - - Square RDeltas[] = { DELTA_N, DELTA_E, DELTA_S, DELTA_W }; - Square BDeltas[] = { DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW }; - - init_magics(RTable, RAttacks, RMagics, RMasks, RShifts, RDeltas, magic_index); - init_magics(BTable, BAttacks, BMagics, BMasks, BShifts, BDeltas, magic_index); - - for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1) - { - PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb(s1, 0); - PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0); - - for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2) - { - Piece pc = (PseudoAttacks[BISHOP][s1] & s2) ? W_BISHOP : - (PseudoAttacks[ROOK][s1] & s2) ? W_ROOK : NO_PIECE; - - if (pc == NO_PIECE) - continue; - - LineBB[s1][s2] = (attacks_bb(pc, s1, 0) & attacks_bb(pc, s2, 0)) | s1 | s2; - BetweenBB[s1][s2] = attacks_bb(pc, s1, SquareBB[s2]) & attacks_bb(pc, s2, SquareBB[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))); + } + BetweenBB[s1][s2] |= s2; + } + } } - namespace { - Bitboard sliding_attack(Square deltas[], Square sq, Bitboard occupied) { - - Bitboard attack = 0; +Bitboard sliding_attack(PieceType pt, Square sq, Bitboard occupied) { - for (int i = 0; i < 4; ++i) - for (Square s = sq + deltas[i]; - is_ok(s) && distance(s, s - deltas[i]) == 1; - s += deltas[i]) - { - attack |= s; + Bitboard attacks = 0; + Direction RookDirections[4] = {NORTH, SOUTH, EAST, WEST}; + Direction BishopDirections[4] = {NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST}; - if (occupied & s) - break; - } + for (Direction d : (pt == ROOK ? RookDirections : BishopDirections)) + { + Square s = sq; + while (safe_destination(s, d) && !(occupied & s)) + attacks |= (s += d); + } - return attack; - } + return attacks; +} - // init_magics() computes all rook and bishop attacks at startup. Magic - // bitboards are used to look up attacks of sliding pieces. As a reference see - // chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we - // use the so called "fancy" approach. +// Computes all rook and bishop attacks at startup. Magic +// bitboards are used to look up attacks of sliding pieces. As a reference see +// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so +// called "fancy" approach. +void init_magics(PieceType pt, Bitboard table[], Magic magics[]) { - void init_magics(Bitboard table[], Bitboard* attacks[], Bitboard magics[], - Bitboard masks[], unsigned shifts[], Square deltas[], Fn index) { + // Optimal PRNG seeds to pick the correct magics in the shortest time + int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020}, + {728, 10316, 55013, 32803, 12281, 15100, 16645, 255}}; - int MagicBoosters[][RANK_NB] = { { 969, 1976, 2850, 542, 2069, 2852, 1708, 164 }, - { 3101, 552, 3555, 926, 834, 26, 2131, 1117 } }; - RKISS rk; Bitboard occupancy[4096], reference[4096], edges, b; - int i, size, booster; - - // attacks[s] is a pointer to the beginning of the attacks table for square 's' - attacks[SQ_A1] = table; + int epoch[4096] = {}, cnt = 0, size = 0; for (Square s = SQ_A1; s <= SQ_H8; ++s) { @@ -269,58 +157,62 @@ namespace { // 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. - masks[s] = sliding_attack(deltas, s, 0) & ~edges; - shifts[s] = (Is64Bit ? 64 : 32) - popcount(masks[s]); + Magic& m = magics[s]; + m.mask = sliding_attack(pt, s, 0) & ~edges; + m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask); + + // Set the offset for the attacks table of the square. We have individual + // table sizes for each square with "Fancy Magic Bitboards". + m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size; // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. b = size = 0; - do { + do + { occupancy[size] = b; - reference[size] = sliding_attack(deltas, s, b); + reference[size] = sliding_attack(pt, s, b); if (HasPext) - attacks[s][_pext_u64(b, masks[s])] = reference[size]; + m.attacks[pext(b, m.mask)] = reference[size]; size++; - b = (b - masks[s]) & masks[s]; + b = (b - m.mask) & m.mask; } while (b); - // Set the offset for the table of the next square. We have individual - // table sizes for each square with "Fancy Magic Bitboards". - if (s < SQ_H8) - attacks[s + 1] = attacks[s] + size; - if (HasPext) continue; - booster = MagicBoosters[Is64Bit][rank_of(s)]; + 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. - do { - do - magics[s] = rk.magic_rand(booster); - while (popcount((magics[s] * masks[s]) >> 56) < 6); - - std::memset(attacks[s], 0, size * sizeof(Bitboard)); + 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. - for (i = 0; i < size; ++i) + // 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) { - Bitboard& attack = attacks[s][index(s, occupancy[i])]; - - if (attack && attack != reference[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; - - assert(reference[i]); - - attack = reference[i]; } - } while (i < size); + } } - } } +} + +} // namespace Stockfish