Build magics on the fly

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Build magics on the fly

Post by mcostalba »

I have removed the magic tables for sliders atatcks from SF. Now magics are built on the fly for both 32 and 64 bits case.

In case of 32 bits I am able to build up all the bishop and rook magics in less then 0.3 secs !! And on my very slow Intel Core 2 at 1,5 Ghz.

The new trick is to use a sequence of PRNG optimized for each rank, so that all the squares of a given rank use a PRNG with an ad-hoc parameter optimally chosen for that rank. This trick, that I have called "magic booster" allow to greatly reduce the time to build up the magics using the "Feeding in Randoms" method:

http://chessprogramming.wikispaces.com/ ... for+Magics


Anyhow here is the code:

Code: Select all

  Bitboard submask(Bitboard mask, int key) {

    Bitboard subMask = 0;
    int bitNum = -1;

    // Extract an unique submask out of a mask according to the given key
    for &#40;Square s = SQ_A1; s <= SQ_H8; s++)
        if &#40;bit_is_set&#40;mask, s&#41; && bit_is_set&#40;key, Square&#40;++bitNum&#41;))
            set_bit&#40;&subMask, s&#41;;

    return subMask;
  &#125;

  Bitboard sliding_attacks&#40;Square sq, Bitboard occupied, Square deltas&#91;&#93;, Bitboard excluded&#41; &#123;

    Bitboard attacks = 0;

    for &#40;int i = 0; i < 4; i++)
    &#123;
        Square s = sq + deltas&#91;i&#93;;

        while (    square_is_ok&#40;s&#41;
               &&  square_distance&#40;s, s - deltas&#91;i&#93;) == 1
               && !bit_is_set&#40;excluded, s&#41;)
        &#123;
            set_bit&#40;&attacks, s&#41;;

            if &#40;bit_is_set&#40;occupied, s&#41;)
                break;

            s += deltas&#91;i&#93;;
        &#125;
    &#125;
    return attacks;
  &#125;

  template<bool Is64>
  Bitboard pick_magic&#40;Bitboard mask, RKISS& rk, int booster&#41; &#123;

    Bitboard magic;
    int lsb;

    if (!Is64&#41;
        lsb = first_1&#40;mask&#41;;

    // Advance PRNG state of a quantity known to be the optimal to
    // quickly retrieve all the magics.
    for &#40;int i = 0; i < booster; i++)
        rk.rand<Bitboard>();

    while &#40;true&#41;
    &#123;
        magic = rk.rand<Bitboard>() & rk.rand<Bitboard>();
        magic &= Is64 ? rk.rand<Bitboard>() &#58; &#40;rk.rand<Bitboard>() | rk.rand<Bitboard>());

        if (   BitCount8Bit&#91;&#40;mask * magic&#41; >> 56&#93; >= 6
            && &#40;Is64 || BitCount8Bit&#91;&#40;lsb * magic&#41; >> 56&#93;))
            return magic;
    &#125;
  &#125;

  void do_magics&#40;Bitboard magic&#91;&#93;, Bitboard* attack&#91;&#93;, Bitboard attTabl&#91;&#93;,
                 Bitboard mask&#91;&#93;, int shift&#91;&#93;, Square deltas&#91;&#93;) &#123;

    const int  MagicBoosters32&#91;&#93; = &#123; 43, 53, 76, 17, 51, 65, 55, 23 &#125;;
    const int  MagicBoosters64&#91;&#93; = &#123; 26, 21, 21, 32, 31,  9,  5, 11 &#125;;

    RKISS rk;
    Bitboard occupancy&#91;4096&#93;, proofs&#91;4096&#93;, excluded;
    int key, maxKey, index, booster, offset = 0;

    for &#40;Square s = SQ_A1; s <= SQ_H8; s++)
    &#123;
        excluded = (&#40;Rank1BB | Rank8BB&#41; & ~rank_bb&#40;s&#41;) | (&#40;FileABB | FileHBB&#41; & ~file_bb&#40;s&#41;);

        attack&#91;s&#93; = &attTabl&#91;offset&#93;;
        mask&#91;s&#93;   = sliding_attacks&#40;s, EmptyBoardBB, deltas, excluded&#41;;
        shift&#91;s&#93;  = &#40;CpuIs64Bit ? 64 &#58; 32&#41; - count_1s<CNT64>&#40;mask&#91;s&#93;);

        maxKey = 1 << count_1s<CNT32>&#40;mask&#91;s&#93;);
        booster = CpuIs64Bit ? MagicBoosters64&#91;square_rank&#40;s&#41;&#93; &#58; MagicBoosters32&#91;square_rank&#40;s&#41;&#93;;

        // First compute occupancy and attacks for square 's'
        for &#40;key = 0; key < maxKey; key++)
        &#123;
            occupancy&#91;key&#93; = submask&#40;mask&#91;s&#93;, key&#41;;
            proofs&#91;key&#93; = sliding_attacks&#40;s, occupancy&#91;key&#93;, deltas, EmptyBoardBB&#41;;
        &#125;

        // Then find a possible magic and corresponding attacks
        do &#123;
            magic&#91;s&#93; = pick_magic<CpuIs64Bit>&#40;mask&#91;s&#93;, rk, booster&#41;;
            memset&#40;attack&#91;s&#93;, 0, maxKey * sizeof&#40;Bitboard&#41;);

            for &#40;key = 0; key < maxKey; key++)
            &#123;
                index = CpuIs64Bit ? unsigned&#40;&#40;occupancy&#91;key&#93; * magic&#91;s&#93;) >> shift&#91;s&#93;)
                                   &#58; unsigned&#40;occupancy&#91;key&#93; * magic&#91;s&#93; ^ &#40;occupancy&#91;key&#93; >> 32&#41; * &#40;magic&#91;s&#93; >> 32&#41;) >> shift&#91;s&#93;;

                if (!attack&#91;s&#93;&#91;index&#93;)
                    attack&#91;s&#93;&#91;index&#93; = proofs&#91;key&#93;;

                else if &#40;attack&#91;s&#93;&#91;index&#93; != proofs&#91;key&#93;)
                    break;
            &#125;
        &#125; while &#40;key != maxKey&#41;;

        offset += maxKey;
    &#125;
  &#125;
This is the code to run the machinery:

Code: Select all

  Bitboard RMask&#91;64&#93;;
  Bitboard RMult&#91;64&#93;;
  Bitboard* RAttacks&#91;64&#93;;
  int RShift&#91;64&#93;;

  Bitboard BMask&#91;64&#93;;
  Bitboard BMult&#91;64&#93;;
  Bitboard* BAttacks&#91;64&#93;;
  int BShift&#91;64&#93;;

  Bitboard RAttacksTable&#91;0x19000&#93;;
  Bitboard BAttacksTable&#91;0x1480&#93;;

  void find_magics&#40;) &#123;

      Square RDeltas&#91;&#93; = &#123; DELTA_N,  DELTA_E,  DELTA_S,  DELTA_W  &#125;;
      Square BDeltas&#91;&#93; = &#123; DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW &#125;;

      do_magics&#40;BMult, BAttacks, BAttacksTable, BMask, BShift, BDeltas&#41;;
      do_magics&#40;RMult, RAttacks, RAttacksTable, RMask, RShift, RDeltas&#41;;
  &#125;

And finally this is how magics bitboards are supposed to be used in both 32 and 64 bits cases:

Code: Select all

/// Functions for computing sliding attack bitboards. rook_attacks_bb&#40;),
/// bishop_attacks_bb&#40;) and queen_attacks_bb&#40;) all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.

#if defined&#40;IS_64BIT&#41;

inline Bitboard rook_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  return RAttacks&#91;s&#93;&#91;(&#40;occ & RMask&#91;s&#93;) * RMult&#91;s&#93;) >> RShift&#91;s&#93;&#93;;
&#125;

inline Bitboard bishop_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  return BAttacks&#91;s&#93;&#91;(&#40;occ & BMask&#91;s&#93;) * BMult&#91;s&#93;) >> BShift&#91;s&#93;&#93;;
&#125;

#else // if !defined&#40;IS_64BIT&#41;

inline Bitboard rook_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  Bitboard b = occ & RMask&#91;s&#93;;
  return RAttacks&#91;s&#93;
         &#91;unsigned&#40;int&#40;b&#41; * int&#40;RMult&#91;s&#93;) ^ int&#40;b >> 32&#41; * int&#40;RMult&#91;s&#93; >> 32&#41;) >> RShift&#91;s&#93;&#93;;
&#125;

inline Bitboard bishop_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  Bitboard b = occ & BMask&#91;s&#93;;
  return BAttacks&#91;s&#93;
         &#91;unsigned&#40;int&#40;b&#41; * int&#40;BMult&#91;s&#93;) ^ int&#40;b >> 32&#41; * int&#40;BMult&#91;s&#93; >> 32&#41;) >> BShift&#91;s&#93;&#93;;
&#125;

#endif
Dann Corbit
Posts: 12537
Joined: Wed Mar 08, 2006 8:57 pm
Location: Redmond, WA USA

Re: Build magics on the fly

Post by Dann Corbit »

mcostalba wrote:I have removed the magic tables for sliders atatcks from SF. Now magics are built on the fly for both 32 and 64 bits case.

In case of 32 bits I am able to build up all the bishop and rook magics in less then 0.3 secs !! And on my very slow Intel Core 2 at 1,5 Ghz.

The new trick is to use a sequence of PRNG optimized for each rank, so that all the squares of a given rank use a PRNG with an ad-hoc parameter optimally chosen for that rank. This trick, that I have called "magic booster" allow to greatly reduce the time to build up the magics using the "Feeding in Randoms" method:

http://chessprogramming.wikispaces.com/ ... for+Magics


Anyhow here is the code:

Code: Select all

  Bitboard submask&#40;Bitboard mask, int key&#41; &#123;

    Bitboard subMask = 0;
    int bitNum = -1;

    // Extract an unique submask out of a mask according to the given key
    for &#40;Square s = SQ_A1; s <= SQ_H8; s++)
        if &#40;bit_is_set&#40;mask, s&#41; && bit_is_set&#40;key, Square&#40;++bitNum&#41;))
            set_bit&#40;&subMask, s&#41;;

    return subMask;
  &#125;

  Bitboard sliding_attacks&#40;Square sq, Bitboard occupied, Square deltas&#91;&#93;, Bitboard excluded&#41; &#123;

    Bitboard attacks = 0;

    for &#40;int i = 0; i < 4; i++)
    &#123;
        Square s = sq + deltas&#91;i&#93;;

        while (    square_is_ok&#40;s&#41;
               &&  square_distance&#40;s, s - deltas&#91;i&#93;) == 1
               && !bit_is_set&#40;excluded, s&#41;)
        &#123;
            set_bit&#40;&attacks, s&#41;;

            if &#40;bit_is_set&#40;occupied, s&#41;)
                break;

            s += deltas&#91;i&#93;;
        &#125;
    &#125;
    return attacks;
  &#125;

  template<bool Is64>
  Bitboard pick_magic&#40;Bitboard mask, RKISS& rk, int booster&#41; &#123;

    Bitboard magic;
    int lsb;

    if (!Is64&#41;
        lsb = first_1&#40;mask&#41;;

    // Advance PRNG state of a quantity known to be the optimal to
    // quickly retrieve all the magics.
    for &#40;int i = 0; i < booster; i++)
        rk.rand<Bitboard>();

    while &#40;true&#41;
    &#123;
        magic = rk.rand<Bitboard>() & rk.rand<Bitboard>();
        magic &= Is64 ? rk.rand<Bitboard>() &#58; &#40;rk.rand<Bitboard>() | rk.rand<Bitboard>());

        if (   BitCount8Bit&#91;&#40;mask * magic&#41; >> 56&#93; >= 6
            && &#40;Is64 || BitCount8Bit&#91;&#40;lsb * magic&#41; >> 56&#93;))
            return magic;
    &#125;
  &#125;

  void do_magics&#40;Bitboard magic&#91;&#93;, Bitboard* attack&#91;&#93;, Bitboard attTabl&#91;&#93;,
                 Bitboard mask&#91;&#93;, int shift&#91;&#93;, Square deltas&#91;&#93;) &#123;

    const int  MagicBoosters32&#91;&#93; = &#123; 43, 53, 76, 17, 51, 65, 55, 23 &#125;;
    const int  MagicBoosters64&#91;&#93; = &#123; 26, 21, 21, 32, 31,  9,  5, 11 &#125;;

    RKISS rk;
    Bitboard occupancy&#91;4096&#93;, proofs&#91;4096&#93;, excluded;
    int key, maxKey, index, booster, offset = 0;

    for &#40;Square s = SQ_A1; s <= SQ_H8; s++)
    &#123;
        excluded = (&#40;Rank1BB | Rank8BB&#41; & ~rank_bb&#40;s&#41;) | (&#40;FileABB | FileHBB&#41; & ~file_bb&#40;s&#41;);

        attack&#91;s&#93; = &attTabl&#91;offset&#93;;
        mask&#91;s&#93;   = sliding_attacks&#40;s, EmptyBoardBB, deltas, excluded&#41;;
        shift&#91;s&#93;  = &#40;CpuIs64Bit ? 64 &#58; 32&#41; - count_1s<CNT64>&#40;mask&#91;s&#93;);

        maxKey = 1 << count_1s<CNT32>&#40;mask&#91;s&#93;);
        booster = CpuIs64Bit ? MagicBoosters64&#91;square_rank&#40;s&#41;&#93; &#58; MagicBoosters32&#91;square_rank&#40;s&#41;&#93;;

        // First compute occupancy and attacks for square 's'
        for &#40;key = 0; key < maxKey; key++)
        &#123;
            occupancy&#91;key&#93; = submask&#40;mask&#91;s&#93;, key&#41;;
            proofs&#91;key&#93; = sliding_attacks&#40;s, occupancy&#91;key&#93;, deltas, EmptyBoardBB&#41;;
        &#125;

        // Then find a possible magic and corresponding attacks
        do &#123;
            magic&#91;s&#93; = pick_magic<CpuIs64Bit>&#40;mask&#91;s&#93;, rk, booster&#41;;
            memset&#40;attack&#91;s&#93;, 0, maxKey * sizeof&#40;Bitboard&#41;);

            for &#40;key = 0; key < maxKey; key++)
            &#123;
                index = CpuIs64Bit ? unsigned&#40;&#40;occupancy&#91;key&#93; * magic&#91;s&#93;) >> shift&#91;s&#93;)
                                   &#58; unsigned&#40;occupancy&#91;key&#93; * magic&#91;s&#93; ^ &#40;occupancy&#91;key&#93; >> 32&#41; * &#40;magic&#91;s&#93; >> 32&#41;) >> shift&#91;s&#93;;

                if (!attack&#91;s&#93;&#91;index&#93;)
                    attack&#91;s&#93;&#91;index&#93; = proofs&#91;key&#93;;

                else if &#40;attack&#91;s&#93;&#91;index&#93; != proofs&#91;key&#93;)
                    break;
            &#125;
        &#125; while &#40;key != maxKey&#41;;

        offset += maxKey;
    &#125;
  &#125;
This is the code to run the machinery:

Code: Select all

  Bitboard RMask&#91;64&#93;;
  Bitboard RMult&#91;64&#93;;
  Bitboard* RAttacks&#91;64&#93;;
  int RShift&#91;64&#93;;

  Bitboard BMask&#91;64&#93;;
  Bitboard BMult&#91;64&#93;;
  Bitboard* BAttacks&#91;64&#93;;
  int BShift&#91;64&#93;;

  Bitboard RAttacksTable&#91;0x19000&#93;;
  Bitboard BAttacksTable&#91;0x1480&#93;;

  void find_magics&#40;) &#123;

      Square RDeltas&#91;&#93; = &#123; DELTA_N,  DELTA_E,  DELTA_S,  DELTA_W  &#125;;
      Square BDeltas&#91;&#93; = &#123; DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW &#125;;

      do_magics&#40;BMult, BAttacks, BAttacksTable, BMask, BShift, BDeltas&#41;;
      do_magics&#40;RMult, RAttacks, RAttacksTable, RMask, RShift, RDeltas&#41;;
  &#125;

And finally this is how magics bitboards are supposed to be used in both 32 and 64 bits cases:

Code: Select all

/// Functions for computing sliding attack bitboards. rook_attacks_bb&#40;),
/// bishop_attacks_bb&#40;) and queen_attacks_bb&#40;) all take a square and a
/// bitboard of occupied squares as input, and return a bitboard representing
/// all squares attacked by a rook, bishop or queen on the given square.

#if defined&#40;IS_64BIT&#41;

inline Bitboard rook_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  return RAttacks&#91;s&#93;&#91;(&#40;occ & RMask&#91;s&#93;) * RMult&#91;s&#93;) >> RShift&#91;s&#93;&#93;;
&#125;

inline Bitboard bishop_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  return BAttacks&#91;s&#93;&#91;(&#40;occ & BMask&#91;s&#93;) * BMult&#91;s&#93;) >> BShift&#91;s&#93;&#93;;
&#125;

#else // if !defined&#40;IS_64BIT&#41;

inline Bitboard rook_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  Bitboard b = occ & RMask&#91;s&#93;;
  return RAttacks&#91;s&#93;
         &#91;unsigned&#40;int&#40;b&#41; * int&#40;RMult&#91;s&#93;) ^ int&#40;b >> 32&#41; * int&#40;RMult&#91;s&#93; >> 32&#41;) >> RShift&#91;s&#93;&#93;;
&#125;

inline Bitboard bishop_attacks_bb&#40;Square s, Bitboard occ&#41; &#123;
  Bitboard b = occ & BMask&#91;s&#93;;
  return BAttacks&#91;s&#93;
         &#91;unsigned&#40;int&#40;b&#41; * int&#40;BMult&#91;s&#93;) ^ int&#40;b >> 32&#41; * int&#40;BMult&#91;s&#93; >> 32&#41;) >> BShift&#91;s&#93;&#93;;
&#125;

#endif
I am sure that this is a dumb question, but why build them each time? Can't you just generate them once and them use them, or is there benefit to regeneration each time (I suppose a little randomness is introduced in that way)?
Sven
Posts: 4052
Joined: Thu May 15, 2008 9:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: Build magics on the fly

Post by Sven »

Dann Corbit wrote:
mcostalba wrote:I have removed the magic tables for sliders atatcks from SF. Now magics are built on the fly for both 32 and 64 bits case.
[...]
I am sure that this is a dumb question, but why build them each time? Can't you just generate them once and them use them, or is there benefit to regeneration each time (I suppose a little randomness is introduced in that way)?
I would assume that "on the fly" means the same as you propose, i.e. the new code to build magics will be called once when magics are needed for the first time, as opposed to using a static table. By rebuilding magics in the beginning of each new search SF would accept an 18 sec disadvantage in each 60-moves game, for instance, which is certainly not intended, and would also break the ability to play short TC games. So I don't believe this is what Marco does.

Sven
UncombedCoconut
Posts: 319
Joined: Fri Dec 18, 2009 11:40 am
Location: Naperville, IL

Re: Build magics on the fly

Post by UncombedCoconut »

The idea is to use a trick to reduce the penalty from 18s to a tiny fraction of 1s. If this penalty in strength (~0 ELO except in hyper-hyper-bullet games) is offset by a reduction in code complexity (as measured by lines), Marco will like it.
I'm partially joking; nobody working on SF measures complexity in LoC. There is something more important going on: this change makes SF a bit closer to being self-contained in the algorithms it uses. If you replace an arbitrary pre-computed table of constants with an algorithmically selected table (generated in a somewhat arbitrary way), your collection of source files becomes easier to understand. On the other hand, I'd prefer even more if SF kept its attack tables in binary form but used an auxiliary program at build-time to generate it. That would probably be the best of both worlds.* But unless you're using game-in-1.0s TC to measure ELO increments, the difference is nothing but a matter of taste.

*In general I think SF would benefit from making code longer but more modular, so that the length of its "code one must read to understand the algorithms completely" would be minimized. For instance, it could use a boost::bimap instead of PieceLetters, get longer by including a semi-standard header, and get shorter in application-specific code.
Daniel Shawul
Posts: 4185
Joined: Tue Mar 14, 2006 11:34 am
Location: Ethiopia

Re: Build magics on the fly

Post by Daniel Shawul »

Why are we talking about ELO (unless I am missing something) ? The engine's clock will not start unless it sends done=1 (or isready in uci). So making magic initialization faster only helps startup time. Also once the engine is started the tables gets reused in a new game (-xresue for winboard). Scorpio wouldn't survive fast bullet games if the time it takes loading of 5 men gets counted...

Also the quicker you calculate the tables, the less optimal (less dense) the magics tables are. It also does not help in accelerating the search for optimal magics (SOMA :) ) since it is just an optimized sequence of randoms to get a random magic quicker than usual..
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Build magics on the fly

Post by mcostalba »

Daniel Shawul wrote:Why are we talking about ELO (unless I am missing something) ? The engine's clock will not start unless it sends done=1 (or isready in uci). So making magic initialization faster only helps startup time. Also once the engine is started the tables gets reused in a new game (-xresue for winboard). Scorpio wouldn't survive fast bullet games if the time it takes loading of 5 men gets counted...

Also the quicker you calculate the tables, the less optimal (less dense) the magics tables are. It also does not help in accelerating the search for optimal magics (SOMA :) ) since it is just an optimized sequence of randoms to get a random magic quicker than usual..
Yes, tables are computed at startup initialization, before engine sends the UCI "isready" command, so the time it takes to compute the magics has no impact on SF strength with any TC.

Magic bitboards in SF use the variable shift ("fancy") approach, but there is no aim at finding the best magics, i.e. magics that allow to use a smaller shift. So the shift value depends only on the number of ones in the pre-mask.

I am interested to find even quicker ways to compute the magics, I am experimenting with rotating the candidate magic by a given number instead of advance the PRNG state, the results in number of trials needed is more or less the same, but the code is faster becuase a couple of shifts (to rotate the magic) are faster then a loop across PRNG generator.

I undestand the suggest of Justin, but I would prefer to keep the algorithm to calculate the magics inside SF for 2 reasons: it is very small, adds just about 10 lines of code, and I want SF documenting how magics are created for reference and learning purposes when sources are read by engine programmers.
mcostalba
Posts: 2684
Joined: Sat Jun 14, 2008 9:17 pm

Re: Build magics on the fly

Post by mcostalba »

This is probably the last iteration at this exercise. I use double rotate as magic boosters now and this greatly speedups magic finding that now takes about 0.1 secs on my slow notebook.

This is the code needed to both find the magics and to initialize the bitboards at SF startup.

Code: Select all

Bitboard sliding_attacks&#40;Square sq, Bitboard occupied, Square delta&#91;&#93;) &#123;

  Bitboard attacks = 0;

  for &#40;int i = 0; i < 4; i++)
  &#123;
      Square s = sq + delta&#91;i&#93;;

      while &#40;square_is_ok&#40;s&#41; && square_distance&#40;s, s - delta&#91;i&#93;) == 1&#41;
      &#123;
          set_bit&#40;&attacks, s&#41;;

          if &#40;bit_is_set&#40;occupied, s&#41;)
              break;

          s += delta&#91;i&#93;;
      &#125;
  &#125;
  return attacks;
&#125;

Bitboard pick_magic&#40;Bitboard mask, RKISS& rk, int booster&#41; &#123;

  Bitboard magic;

  // Values s1 and s2 are used to rotate the candidate magic of a
  // quantity known to be the optimal to quickly find the magics.
  int s1 = booster & 63, s2 = &#40;booster >> 6&#41; & 63;

  while &#40;true&#41;
  &#123;
      magic = rk.rand<Bitboard>();
      magic = &#40;magic >> s1&#41; | &#40;magic << &#40;64 - s1&#41;);
      magic &= rk.rand<Bitboard>();
      magic = &#40;magic >> s2&#41; | &#40;magic << &#40;64 - s2&#41;);
      magic &= rk.rand<Bitboard>();

      if &#40;BitCount8Bit&#91;&#40;mask * magic&#41; >> 56&#93; >= 6&#41;
          return magic;
  &#125;
&#125;

void init_sliding_attacks&#40;Bitboard magic&#91;&#93;, Bitboard* attack&#91;&#93;, Bitboard attTable&#91;&#93;,
                          Bitboard mask&#91;&#93;, int shift&#91;&#93;, Square delta&#91;&#93;) &#123;

  const int  MagicBoosters&#91;&#93;&#91;8&#93; = &#123; &#123; 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 &#125;,
                                    &#123; 1059, 3608,  605, 3234, 3326,   38, 2029, 3043 &#125; &#125;;
  RKISS rk;
  Bitboard occupancy&#91;4096&#93;, reference&#91;4096&#93;, edges, b;
  int key, maxKey, index, booster, offset = 0;

  for &#40;Square s = SQ_A1; s <= SQ_H8; s++)
  &#123;
      edges = (&#40;Rank1BB | Rank8BB&#41; & ~rank_bb&#40;s&#41;) | (&#40;FileABB | FileHBB&#41; & ~file_bb&#40;s&#41;);

      attack&#91;s&#93; = &attTable&#91;offset&#93;;
      mask&#91;s&#93;   = sliding_attacks&#40;s, EmptyBoardBB, delta&#41; & ~edges;
      shift&#91;s&#93;  = &#40;CpuIs64Bit ? 64 &#58; 32&#41; - count_1s<CNT32_MAX15>&#40;mask&#91;s&#93;);

      // Use Carry-Rippler trick to enumerate all subsets of mask&#91;s&#93;
      b = maxKey = 0;
      do &#123;
          occupancy&#91;maxKey&#93; = b;
          reference&#91;maxKey++&#93; = sliding_attacks&#40;s, b, delta&#41;;
          b = &#40;b - mask&#91;s&#93;) & mask&#91;s&#93;;
      &#125; while &#40;b&#41;;

      offset += maxKey;
      booster = MagicBoosters&#91;CpuIs64Bit&#93;&#91;square_rank&#40;s&#41;&#93;;

      // Then find a possible magic and the corresponding attacks
      do &#123;
          magic&#91;s&#93; = pick_magic&#40;mask&#91;s&#93;, rk, booster&#41;;
          memset&#40;attack&#91;s&#93;, 0, maxKey * sizeof&#40;Bitboard&#41;);

          for &#40;key = 0; key < maxKey; key++)
          &#123;
              index = CpuIs64Bit ? unsigned&#40;&#40;occupancy&#91;key&#93; * magic&#91;s&#93;) >> shift&#91;s&#93;)
                                 &#58; unsigned&#40;occupancy&#91;key&#93; * magic&#91;s&#93; ^ &#40;occupancy&#91;key&#93; >> 32&#41; * &#40;magic&#91;s&#93; >> 32&#41;) >> shift&#91;s&#93;;

              if (!attack&#91;s&#93;&#91;index&#93;)
                  attack&#91;s&#93;&#91;index&#93; = reference&#91;key&#93;;

              else if &#40;attack&#91;s&#93;&#91;index&#93; != reference&#91;key&#93;)
                  break;
          &#125;
      &#125; while &#40;key != maxKey&#41;;
  &#125;
&#125;