Skip to content

Commit ed1f9a2

Browse files
committed
Merge remote-tracking branch 'niklasf/dense-magics-2' into ddugovic
2 parents beadb6c + fc04238 commit ed1f9a2

File tree

2 files changed

+183
-78
lines changed

2 files changed

+183
-78
lines changed

src/bitboard.cpp

Lines changed: 179 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,150 @@ namespace {
5454

5555
int MSBTable[256]; // To implement software msb()
5656
Square BSFTable[SQUARE_NB]; // To implement software bitscan
57-
Bitboard RookTable[0x19000]; // To store rook attacks
58-
Bitboard BishopTable[0x1480]; // To store bishop attacks
5957

60-
void init_magics(Bitboard table[], Magic magics[], Direction directions[]);
58+
Bitboard AttackTable[HasPext ? 107648 : 88772] = { 0 };
59+
60+
struct MagicInit {
61+
Bitboard magic;
62+
unsigned offset;
63+
};
64+
65+
MagicInit BishopMagicInit[SQUARE_NB] = {
66+
{ 0x007fbfbfbfbfbfffu, 5378 },
67+
{ 0x0000a060401007fcu, 4093 },
68+
{ 0x0001004008020000u, 4314 },
69+
{ 0x0000806004000000u, 6587 },
70+
{ 0x0000100400000000u, 6491 },
71+
{ 0x000021c100b20000u, 6330 },
72+
{ 0x0000040041008000u, 5609 },
73+
{ 0x00000fb0203fff80u, 22236 },
74+
{ 0x0000040100401004u, 6106 },
75+
{ 0x0000020080200802u, 5625 },
76+
{ 0x0000004010202000u, 16785 },
77+
{ 0x0000008060040000u, 16817 },
78+
{ 0x0000004402000000u, 6842 },
79+
{ 0x0000000801008000u, 7003 },
80+
{ 0x000007efe0bfff80u, 4197 },
81+
{ 0x0000000820820020u, 7356 },
82+
{ 0x0000400080808080u, 4602 },
83+
{ 0x00021f0100400808u, 4538 },
84+
{ 0x00018000c06f3fffu, 29531 },
85+
{ 0x0000258200801000u, 45393 },
86+
{ 0x0000240080840000u, 12420 },
87+
{ 0x000018000c03fff8u, 15763 },
88+
{ 0x00000a5840208020u, 5050 },
89+
{ 0x0000020008208020u, 4346 },
90+
{ 0x0000804000810100u, 6074 },
91+
{ 0x0001011900802008u, 7866 },
92+
{ 0x0000804000810100u, 32139 },
93+
{ 0x000100403c0403ffu, 57673 },
94+
{ 0x00078402a8802000u, 55365 },
95+
{ 0x0000101000804400u, 15818 },
96+
{ 0x0000080800104100u, 5562 },
97+
{ 0x00004004c0082008u, 6390 },
98+
{ 0x0001010120008020u, 7930 },
99+
{ 0x000080809a004010u, 13329 },
100+
{ 0x0007fefe08810010u, 7170 },
101+
{ 0x0003ff0f833fc080u, 27267 },
102+
{ 0x007fe08019003042u, 53787 },
103+
{ 0x003fffefea003000u, 5097 },
104+
{ 0x0000101010002080u, 6643 },
105+
{ 0x0000802005080804u, 6138 },
106+
{ 0x0000808080a80040u, 7418 },
107+
{ 0x0000104100200040u, 7898 },
108+
{ 0x0003ffdf7f833fc0u, 42012 },
109+
{ 0x0000008840450020u, 57350 },
110+
{ 0x00007ffc80180030u, 22813 },
111+
{ 0x007fffdd80140028u, 56693 },
112+
{ 0x00020080200a0004u, 5818 },
113+
{ 0x0000101010100020u, 7098 },
114+
{ 0x0007ffdfc1805000u, 4451 },
115+
{ 0x0003ffefe0c02200u, 4709 },
116+
{ 0x0000000820806000u, 4794 },
117+
{ 0x0000000008403000u, 13364 },
118+
{ 0x0000000100202000u, 4570 },
119+
{ 0x0000004040802000u, 4282 },
120+
{ 0x0004010040100400u, 14964 },
121+
{ 0x00006020601803f4u, 4026 },
122+
{ 0x0003ffdfdfc28048u, 4826 },
123+
{ 0x0000000820820020u, 7354 },
124+
{ 0x0000000008208060u, 4848 },
125+
{ 0x0000000000808020u, 15946 },
126+
{ 0x0000000001002020u, 14932 },
127+
{ 0x0000000401002008u, 16588 },
128+
{ 0x0000004040404040u, 6905 },
129+
{ 0x007fff9fdf7ff813u, 16076 }
130+
};
131+
132+
MagicInit RookMagicInit[SQUARE_NB] = {
133+
{ 0x00280077ffebfffeu, 26304 },
134+
{ 0x2004010201097fffu, 35520 },
135+
{ 0x0010020010053fffu, 38592 },
136+
{ 0x0040040008004002u, 8026 },
137+
{ 0x7fd00441ffffd003u, 22196 },
138+
{ 0x4020008887dffffeu, 80870 },
139+
{ 0x004000888847ffffu, 76747 },
140+
{ 0x006800fbff75fffdu, 30400 },
141+
{ 0x000028010113ffffu, 11115 },
142+
{ 0x0020040201fcffffu, 18205 },
143+
{ 0x007fe80042ffffe8u, 53577 },
144+
{ 0x00001800217fffe8u, 62724 },
145+
{ 0x00001800073fffe8u, 34282 },
146+
{ 0x00001800e05fffe8u, 29196 },
147+
{ 0x00001800602fffe8u, 23806 },
148+
{ 0x000030002fffffa0u, 49481 },
149+
{ 0x00300018010bffffu, 2410 },
150+
{ 0x0003000c0085fffbu, 36498 },
151+
{ 0x0004000802010008u, 24478 },
152+
{ 0x0004002020020004u, 10074 },
153+
{ 0x0001002002002001u, 79315 },
154+
{ 0x0001001000801040u, 51779 },
155+
{ 0x0000004040008001u, 13586 },
156+
{ 0x0000006800cdfff4u, 19323 },
157+
{ 0x0040200010080010u, 70612 },
158+
{ 0x0000080010040010u, 83652 },
159+
{ 0x0004010008020008u, 63110 },
160+
{ 0x0000040020200200u, 34496 },
161+
{ 0x0002008010100100u, 84966 },
162+
{ 0x0000008020010020u, 54341 },
163+
{ 0x0000008020200040u, 60421 },
164+
{ 0x0000820020004020u, 86402 },
165+
{ 0x00fffd1800300030u, 50245 },
166+
{ 0x007fff7fbfd40020u, 76622 },
167+
{ 0x003fffbd00180018u, 84676 },
168+
{ 0x001fffde80180018u, 78757 },
169+
{ 0x000fffe0bfe80018u, 37346 },
170+
{ 0x0001000080202001u, 370 },
171+
{ 0x0003fffbff980180u, 42182 },
172+
{ 0x0001fffdff9000e0u, 45385 },
173+
{ 0x00fffefeebffd800u, 61659 },
174+
{ 0x007ffff7ffc01400u, 12790 },
175+
{ 0x003fffbfe4ffe800u, 16762 },
176+
{ 0x001ffff01fc03000u, 0 },
177+
{ 0x000fffe7f8bfe800u, 38380 },
178+
{ 0x0007ffdfdf3ff808u, 11098 },
179+
{ 0x0003fff85fffa804u, 21803 },
180+
{ 0x0001fffd75ffa802u, 39189 },
181+
{ 0x00ffffd7ffebffd8u, 58628 },
182+
{ 0x007fff75ff7fbfd8u, 44116 },
183+
{ 0x003fff863fbf7fd8u, 78357 },
184+
{ 0x001fffbfdfd7ffd8u, 44481 },
185+
{ 0x000ffff810280028u, 64134 },
186+
{ 0x0007ffd7f7feffd8u, 41759 },
187+
{ 0x0003fffc0c480048u, 1394 },
188+
{ 0x0001ffffafd7ffd8u, 40910 },
189+
{ 0x00ffffe4ffdfa3bau, 66516 },
190+
{ 0x007fffef7ff3d3dau, 3897 },
191+
{ 0x003fffbfdfeff7fau, 3930 },
192+
{ 0x001fffeff7fbfc22u, 72934 },
193+
{ 0x0000020408001001u, 72662 },
194+
{ 0x0007fffeffff77fdu, 56325 },
195+
{ 0x0003ffffbf7dfeecu, 66501 },
196+
{ 0x0001ffff9dffa333u, 14826 }
197+
};
198+
199+
Bitboard relevant_occupancies(Direction directions[], Square s);
200+
void init_magics(MagicInit init[], Magic magics[], Direction directions[], unsigned shift);
61201

62202
// bsf_index() returns the index into BSFTable[] to look up the bitscan. Uses
63203
// Matt Taylor's folding for 32 bit case, extended to 64 bit by Kim Walisch.
@@ -205,8 +345,25 @@ void Bitboards::init() {
205345
Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
206346
Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
207347

208-
init_magics(RookTable, RookMagics, RookDirections);
209-
init_magics(BishopTable, BishopMagics, BishopDirections);
348+
if (HasPext)
349+
{
350+
unsigned offset = 0;
351+
352+
for (Square s = SQ_A1; s <= SQ_H8; ++s)
353+
{
354+
RookMagicInit[s].offset = offset;
355+
offset += 1 << popcount(relevant_occupancies(RookDirections, s));
356+
}
357+
358+
for (Square s = SQ_A1; s <= SQ_H8; ++s)
359+
{
360+
BishopMagicInit[s].offset = offset;
361+
offset += 1 << popcount(relevant_occupancies(BishopDirections, s));
362+
}
363+
}
364+
365+
init_magics(RookMagicInit, RookMagics, RookDirections, 12);
366+
init_magics(BishopMagicInit, BishopMagics, BishopDirections, 9);
210367

211368
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
212369
{
@@ -270,84 +427,35 @@ namespace {
270427
return attack;
271428
}
272429

430+
Bitboard relevant_occupancies(Direction directions[], Square s) {
273431

274-
// init_magics() computes all rook and bishop attacks at startup. Magic
275-
// bitboards are used to look up attacks of sliding pieces. As a reference see
276-
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
277-
// use the so called "fancy" approach.
278-
279-
void init_magics(Bitboard table[], Magic magics[], Direction directions[]) {
432+
Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
433+
return sliding_attack(directions, s, 0) & ~edges;
434+
}
280435

281-
// Optimal PRNG seeds to pick the correct magics in the shortest time
282-
int seeds[][RANK_NB] = { { 8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020 },
283-
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
436+
// Magic bitboards are used to look up attacks of sliding pieces.
437+
// init_magics() initializes the attack tables from precomputed fixed shift
438+
// magics with overlapping index ranges:
439+
// <https://chessprogramming.wikispaces.com/Magic+Bitboards#FixedShiftFancy>
284440

285-
Bitboard occupancy[4096], reference[4096], edges, b;
286-
int epoch[4096] = {}, cnt = 0, size = 0;
441+
void init_magics(MagicInit init[], Magic magics[], Direction directions[], unsigned shift) {
287442

288443
for (Square s = SQ_A1; s <= SQ_H8; ++s)
289444
{
290-
// Board edges are not considered in the relevant occupancies
291-
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
292-
293-
// Given a square 's', the mask is the bitboard of sliding attacks from
294-
// 's' computed on an empty board. The index must be big enough to contain
295-
// all the attacks for each possible subset of the mask and so is 2 power
296-
// the number of 1s of the mask. Hence we deduce the size of the shift to
297-
// apply to the 64 or 32 bits word to get the index.
298445
Magic& m = magics[s];
299-
m.mask = sliding_attack(directions, s, 0) & ~edges;
300-
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
301446

302-
// Set the offset for the attacks table of the square. We have individual
303-
// table sizes for each square with "Fancy Magic Bitboards".
304-
m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
447+
m.magic = init[s].magic;
448+
m.mask = relevant_occupancies(directions, s);
449+
m.attacks = AttackTable + init[s].offset;
305450

306-
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
307-
// store the corresponding sliding attack bitboard in reference[].
308-
b = size = 0;
451+
Bitboard b = 0;
309452
do {
310-
occupancy[size] = b;
311-
reference[size] = sliding_attack(directions, s, b);
312-
313-
if (HasPext)
314-
m.attacks[pext(b, m.mask)] = reference[size];
315-
316-
size++;
453+
unsigned idx = HasPext ? pext(b, m.mask) : (m.magic * b) >> (64 - shift);
454+
Bitboard attack = sliding_attack(directions, s, b);
455+
assert(!m.attacks[idx] || m.attacks[idx] == attack);
456+
m.attacks[idx] = attack;
317457
b = (b - m.mask) & m.mask;
318458
} while (b);
319-
320-
if (HasPext)
321-
continue;
322-
323-
PRNG rng(seeds[Is64Bit][rank_of(s)]);
324-
325-
// Find a magic for square 's' picking up an (almost) random number
326-
// until we find the one that passes the verification test.
327-
for (int i = 0; i < size; )
328-
{
329-
for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
330-
m.magic = rng.sparse_rand<Bitboard>();
331-
332-
// A good magic must map every possible occupancy to an index that
333-
// looks up the correct sliding attack in the attacks[s] database.
334-
// Note that we build up the database for square 's' as a side
335-
// effect of verifying the magic. Keep track of the attempt count
336-
// and save it in epoch[], little speed-up trick to avoid resetting
337-
// m.attacks[] after every failed attempt.
338-
for (++cnt, i = 0; i < size; ++i)
339-
{
340-
unsigned idx = m.index(occupancy[i]);
341-
342-
if (epoch[idx] < cnt)
343-
{
344-
epoch[idx] = cnt;
345-
m.attacks[idx] = reference[i];
346-
}
347-
else if (m.attacks[idx] != reference[i])
348-
break;
349-
}
350-
}
351459
}
352460
}
353461
}

src/bitboard.h

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,17 @@ struct Magic {
9090
Bitboard mask;
9191
Bitboard magic;
9292
Bitboard* attacks;
93-
unsigned shift;
9493

9594
// Compute the attack's index using the 'magic bitboards' approach
95+
template<PieceType Pt>
9696
unsigned index(Bitboard occupied) const {
9797

9898
if (HasPext)
9999
return unsigned(pext(occupied, mask));
100100

101-
if (Is64Bit)
102-
return unsigned(((occupied & mask) * magic) >> shift);
101+
unsigned shift = 64 - (Pt == ROOK ? 12 : 9);
103102

104-
unsigned lo = unsigned(occupied) & unsigned(mask);
105-
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
106-
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
103+
return unsigned(((occupied & mask) * magic) >> shift);
107104
}
108105
};
109106

@@ -264,7 +261,7 @@ template<PieceType Pt>
264261
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
265262

266263
const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
267-
return m.attacks[m.index(occupied)];
264+
return m.attacks[m.index<Pt>(occupied)];
268265
}
269266

270267
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {

0 commit comments

Comments
 (0)