// 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. private static void init_magics( BitboardT[][] attacks, BitboardT[] magics, BitboardT[] masks, uint[] shifts, SquareT[] deltas, Utils.Fn index) { int[][] seeds = { new[] {8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020}, new[] {728, 10316, 55013, 32803, 12281, 15100, 16645, 255} }; var occupancy = new BitboardT[4096]; var reference = new BitboardT[4096]; var age = new int[4096]; int current = 0; for (var s = Square.SQ_A1; s <= Square.SQ_H8; ++s) { // Board edges are not considered in the relevant occupancies var edges = ((Bitboard.Rank1BB | Bitboard.Rank8BB) & ~Utils.rank_bb_St(s) | ((Bitboard.FileABB | Bitboard.FileHBB) & ~Utils.file_bb_St(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. masks[s] = sliding_attack(deltas, s, Bitboard.Create(0)) & ~edges; #if X64 shifts[(int)s] = (uint) (64 - Bitcount.popcount_Max15(masks[(int)s])); #else shifts[s] = (uint)(32 - Bitcount.popcount_Max15(masks[s])); #endif // Use Carry-Rippler trick to enumerate all subsets of masks[s] and // store the corresponding sliding attack bitboard in reference[]. var b = Bitboard.Create(0); var size = 0; do { occupancy[size] = b; reference[size] = sliding_attack(deltas, s, b); // if (HasPext) // attacks[s][pext(b, masks[s])] = reference[size]; size++; b = (b - masks[s]) & masks[s]; } while (b != 0); // Set the offset for the table of the next square. We have individual // table sizes for each square with "Fancy Magic Bitboards". attacks[s] = new BitboardT[size]; // if (HasPext) // continue; #if X64 var rng = new PRNG((ulong) seeds[1][(int)Square.rank_of(s)]); #else var rng = new PRNG((ulong)seeds[0][Square.rank_of(s)]); #endif // Find a magic for square 's' picking up an (almost) random number // until we find the one that passes the verification test. int i; do { do { magics[s] = Bitboard.Create(rng.sparse_rand()); } while (Bitcount.popcount_Max15((magics[s]*masks[s]) >> 56) < 6); Array.Clear(attacks[s], 0, size); // 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 (++current, i = 0; i < size; ++i) { var idx = index(s, occupancy[i]); if (age[idx] < current) { age[idx] = current; attacks[s][idx] = reference[i]; } else if (attacks[s][idx] != reference[i]) { break; } } } while (i < size); } }