namespace {
+/// Fast generator for magic multipliers.
+/// Works by purposeful backtracking rather than random trying:
+/// recursively maps not-already-placed mask bits in the lookup key without spoiling
+/// any bits that were placed earlier.
+
+Bitboard puzzle(Bitboard key, Bitboard maskBitsToMap, Bitboard magic, int keyStart)
+{
+ int i, k;
+ Bitboard keyBits = AllSquares << keyStart;
+
+ for(k = SQ_MAX; ~maskBitsToMap & Square(k); k--) {} // find mask bit to map
+
+ for(i = (k > keyStart ? k : keyStart); i <= SQUARE_BIT_MASK; i++)
+ {
+ int shift = i - k;
+ Bitboard newBits = maskBitsToMap << shift;
+
+ if(key & newBits & keyBits) continue; // does not fit in, shift more
+
+ Bitboard newKey = key + newBits;
+ Bitboard changedKeyBits = (newKey ^ key) & keyBits;
+
+ if(changedKeyBits & key) continue; // carry spoiled key
+
+ Bitboard newMagic = magic | Bitboard(1) << shift;
+ Bitboard remainingMask = maskBitsToMap - (changedKeyBits >> shift);
+
+ if(remainingMask)
+ newMagic = puzzle(newKey, remainingMask, newMagic, keyStart);
+
+ if(newMagic) return newMagic; // done
+ }
+
+ return 0; // fail
+}
+
// 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
// www.chessprogramming.org/Magic_Bitboards. In particular, here we use the so
void init_magics(Bitboard table[], Magic magics[], std::map<DirectionCode, int> directions) {
#endif
- // Optimal PRNG seeds to pick the correct magics in the shortest time
-#ifndef PRECOMPUTED_MAGICS
-#ifdef LARGEBOARDS
- int seeds[][RANK_NB] = { { 734, 10316, 55013, 32803, 12281, 15100, 16645, 255, 346, 89123 },
- { 734, 10316, 55013, 32803, 12281, 15100, 16645, 255, 346, 89123 } };
-#else
- int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
- { 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
-#endif
-#endif
-
Bitboard* occupancy = new Bitboard[1 << (FILE_NB + RANK_NB - 4)];
Bitboard* reference = new Bitboard[1 << (FILE_NB + RANK_NB - 4)];
Bitboard edges, b;
int* epoch = new int[1 << (FILE_NB + RANK_NB - 4)]();
- int cnt = 0, size = 0;
+ int cnt = 0, size = 0, totalSize = 0;
for (Square s = SQ_A1; s <= SQ_MAX; ++s)
{
if (HasPext)
continue;
-#ifndef PRECOMPUTED_MAGICS
- PRNG rng(seeds[Is64Bit][rank_of(s)]);
+#ifdef PRECOMPUTED_MAGICS
+ // With large boards we always use an intelligent first try, as randomly trying until we succeed takes very long
+ if(magicsInit) m.magic = magicsInit[s]; else // use precomputed if there is one
#endif
+ m.magic = puzzle(0, m.mask, 0, m.shift);
- // 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) >> (SQUARE_NB - FILE_NB)) < FILE_NB - 2; )
- {
-#ifdef LARGEBOARDS
-#ifdef PRECOMPUTED_MAGICS
- m.magic = magicsInit[s];
-#else
- m.magic = (rng.sparse_rand<Bitboard>() << 64) ^ rng.sparse_rand<Bitboard>();
-#endif
-#else
- m.magic = rng.sparse_rand<Bitboard>();
-#endif
- }
+ int i;
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
break;
}
}
+ totalSize += size;
}
+#ifndef NDEBUG
+ sync_cout << "# magic_init: size = " << totalSize << "(" << (totalSize*16 / 1024) << " KB)" << sync_endl;
+#endif
delete[] occupancy;
delete[] reference;