1use crate::types::*;
10use std::sync::OnceLock;
11
12const ROOK_MAGICS: [u64; 64] = [
13 0xa8002c000108020,
14 0x6c00049b0002001,
15 0x100200010090040,
16 0x2480041000800801,
17 0x280028004000800,
18 0x900410008040022,
19 0x280020001001080,
20 0x2880002041000080,
21 0xa000800080400034,
22 0x4808020004000,
23 0x2290802004801000,
24 0x411000d00100020,
25 0x402800800040080,
26 0xb000401004208,
27 0x2409000100040200,
28 0x1002100004082,
29 0x22878001e24000,
30 0x1090810021004010,
31 0x801030040200012,
32 0x500808008001000,
33 0xa08018014000880,
34 0x8000808004000200,
35 0x201008080010200,
36 0x801020000441091,
37 0x800080204005,
38 0x1040200040100048,
39 0x120200402082,
40 0xd14880480100080,
41 0x12040280080080,
42 0x100040080020080,
43 0x9020010080800200,
44 0x813241200148449,
45 0x491604001800080,
46 0x100401000402001,
47 0x4820010021001040,
48 0x400402202000812,
49 0x209009005000802,
50 0x810800601800400,
51 0x4301083214000150,
52 0x204026458e001401,
53 0x40204000808000,
54 0x8001008040010020,
55 0x8410820820420010,
56 0x1003001000090020,
57 0x804040008008080,
58 0x12000810020004,
59 0x1000100200040208,
60 0x430000a044020001,
61 0x280009023410300,
62 0xe0100040002240,
63 0x200100401700,
64 0x2244100408008080,
65 0x8000400801980,
66 0x2000810040200,
67 0x8010100228810400,
68 0x2000009044210200,
69 0x4080008040102101,
70 0x40002080411d01,
71 0x2005524060000901,
72 0x502001008400422,
73 0x489a000810200402,
74 0x1004400080a13,
75 0x4000011008020084,
76 0x26002114058042,
77];
78
79const BISHOP_MAGICS: [u64; 64] = [
80 0x89a1121896040240,
81 0x2004844802002010,
82 0x2068080051921000,
83 0x62880a0220200808,
84 0x4042004000000,
85 0x100822020200011,
86 0xc00444222012000a,
87 0x28808801216001,
88 0x400492088408100,
89 0x201c401040c0084,
90 0x840800910a0010,
91 0x82080240060,
92 0x2000840504006000,
93 0x30010c4108405004,
94 0x1008005410080802,
95 0x8144042209100900,
96 0x208081020014400,
97 0x4800201208ca00,
98 0xf18140408012008,
99 0x1004002802102001,
100 0x841000820080811,
101 0x40200200a42008,
102 0x800054042000,
103 0x88010400410c9000,
104 0x520040470104290,
105 0x1004040051500081,
106 0x2002081833080021,
107 0x400c00c010142,
108 0x941408200c002000,
109 0x658810000806011,
110 0x188071040440a00,
111 0x4800404002011c00,
112 0x104442040404200,
113 0x511080202091021,
114 0x4022401120400,
115 0x80c0040400080120,
116 0x8040010040820802,
117 0x480810700020090,
118 0x102008e00040242,
119 0x809005202050100,
120 0x8002024220104080,
121 0x431008804142000,
122 0x19001802081400,
123 0x200014208040080,
124 0x3308082008200100,
125 0x41010500040c020,
126 0x4012020c04210308,
127 0x208220a202004080,
128 0x111040120082000,
129 0x6803040141280a00,
130 0x2101004202410000,
131 0x8200000041108022,
132 0x21082088000,
133 0x2410204010040,
134 0x40100400809000,
135 0x822088220820214,
136 0x40808090012004,
137 0x910224040218c9,
138 0x402814422015008,
139 0x90014004842410,
140 0x1000042304105,
141 0x10008830412a00,
142 0x2520081090008908,
143 0x40102000a0a60140,
144];
145
146const ROOK_INDEX_BITS: [u32; 64] = [
147 12, 11, 11, 11, 11, 11, 11, 12, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11,
148 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11, 11, 10, 10, 10, 10, 10, 10, 11,
149 11, 10, 10, 10, 10, 10, 10, 11, 12, 11, 11, 11, 11, 11, 11, 12,
150];
151
152const BISHOP_INDEX_BITS: [u32; 64] = [
153 6, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 5, 5, 5, 5, 7, 9, 9, 7, 5, 5,
154 5, 5, 7, 9, 9, 7, 5, 5, 5, 5, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 5, 5, 5, 5, 5, 5, 6,
155];
156
157pub(crate) const ROOK_MASKS: [Bitboard; 64] = [
158 Bitboard(0x000101010101017e),
159 Bitboard(0x000202020202027c),
160 Bitboard(0x000404040404047a),
161 Bitboard(0x0008080808080876),
162 Bitboard(0x001010101010106e),
163 Bitboard(0x002020202020205e),
164 Bitboard(0x004040404040403e),
165 Bitboard(0x008080808080807e),
166 Bitboard(0x0001010101017e00),
167 Bitboard(0x0002020202027c00),
168 Bitboard(0x0004040404047a00),
169 Bitboard(0x0008080808087600),
170 Bitboard(0x0010101010106e00),
171 Bitboard(0x0020202020205e00),
172 Bitboard(0x0040404040403e00),
173 Bitboard(0x0080808080807e00),
174 Bitboard(0x00010101017e0100),
175 Bitboard(0x00020202027c0200),
176 Bitboard(0x00040404047a0400),
177 Bitboard(0x0008080808760800),
178 Bitboard(0x00101010106e1000),
179 Bitboard(0x00202020205e2000),
180 Bitboard(0x00404040403e4000),
181 Bitboard(0x00808080807e8000),
182 Bitboard(0x000101017e010100),
183 Bitboard(0x000202027c020200),
184 Bitboard(0x000404047a040400),
185 Bitboard(0x0008080876080800),
186 Bitboard(0x001010106e101000),
187 Bitboard(0x002020205e202000),
188 Bitboard(0x004040403e404000),
189 Bitboard(0x008080807e808000),
190 Bitboard(0x0001017e01010100),
191 Bitboard(0x0002027c02020200),
192 Bitboard(0x0004047a04040400),
193 Bitboard(0x0008087608080800),
194 Bitboard(0x0010106e10101000),
195 Bitboard(0x0020205e20202000),
196 Bitboard(0x0040403e40404000),
197 Bitboard(0x0080807e80808000),
198 Bitboard(0x00017e0101010100),
199 Bitboard(0x00027c0202020200),
200 Bitboard(0x00047a0404040400),
201 Bitboard(0x0008760808080800),
202 Bitboard(0x00106e1010101000),
203 Bitboard(0x00205e2020202000),
204 Bitboard(0x00403e4040404000),
205 Bitboard(0x00807e8080808000),
206 Bitboard(0x007e010101010100),
207 Bitboard(0x007c020202020200),
208 Bitboard(0x007a040404040400),
209 Bitboard(0x0076080808080800),
210 Bitboard(0x006e101010101000),
211 Bitboard(0x005e202020202000),
212 Bitboard(0x003e404040404000),
213 Bitboard(0x007e808080808000),
214 Bitboard(0x7e01010101010100),
215 Bitboard(0x7c02020202020200),
216 Bitboard(0x7a04040404040400),
217 Bitboard(0x7608080808080800),
218 Bitboard(0x6e10101010101000),
219 Bitboard(0x5e20202020202000),
220 Bitboard(0x3e40404040404000),
221 Bitboard(0x7e80808080808000),
222];
223
224pub(crate) const BISHOP_MASKS: [Bitboard; 64] = [
225 Bitboard(0x0040201008040200),
226 Bitboard(0x0000402010080400),
227 Bitboard(0x0000004020100a00),
228 Bitboard(0x0000000040221400),
229 Bitboard(0x0000000002442800),
230 Bitboard(0x0000000204085000),
231 Bitboard(0x0000020408102000),
232 Bitboard(0x0002040810204000),
233 Bitboard(0x0020100804020000),
234 Bitboard(0x0040201008040000),
235 Bitboard(0x00004020100a0000),
236 Bitboard(0x0000004022140000),
237 Bitboard(0x0000000244280000),
238 Bitboard(0x0000020408500000),
239 Bitboard(0x0002040810200000),
240 Bitboard(0x0004081020400000),
241 Bitboard(0x0010080402000200),
242 Bitboard(0x0020100804000400),
243 Bitboard(0x004020100a000a00),
244 Bitboard(0x0000402214001400),
245 Bitboard(0x0000024428002800),
246 Bitboard(0x0002040850005000),
247 Bitboard(0x0004081020002000),
248 Bitboard(0x0008102040004000),
249 Bitboard(0x0008040200020400),
250 Bitboard(0x0010080400040800),
251 Bitboard(0x0020100a000a1000),
252 Bitboard(0x0040221400142200),
253 Bitboard(0x0002442800284400),
254 Bitboard(0x0004085000500800),
255 Bitboard(0x0008102000201000),
256 Bitboard(0x0010204000402000),
257 Bitboard(0x0004020002040800),
258 Bitboard(0x0008040004081000),
259 Bitboard(0x00100a000a102000),
260 Bitboard(0x0022140014224000),
261 Bitboard(0x0044280028440200),
262 Bitboard(0x0008500050080400),
263 Bitboard(0x0010200020100800),
264 Bitboard(0x0020400040201000),
265 Bitboard(0x0002000204081000),
266 Bitboard(0x0004000408102000),
267 Bitboard(0x000a000a10204000),
268 Bitboard(0x0014001422400000),
269 Bitboard(0x0028002844020000),
270 Bitboard(0x0050005008040200),
271 Bitboard(0x0020002010080400),
272 Bitboard(0x0040004020100800),
273 Bitboard(0x0000020408102000),
274 Bitboard(0x0000040810204000),
275 Bitboard(0x00000a1020400000),
276 Bitboard(0x0000142240000000),
277 Bitboard(0x0000284402000000),
278 Bitboard(0x0000500804020000),
279 Bitboard(0x0000201008040200),
280 Bitboard(0x0000402010080400),
281 Bitboard(0x0002040810204000),
282 Bitboard(0x0004081020400000),
283 Bitboard(0x000a102040000000),
284 Bitboard(0x0014224000000000),
285 Bitboard(0x0028440200000000),
286 Bitboard(0x0050080402000000),
287 Bitboard(0x0020100804020000),
288 Bitboard(0x0040201008040200),
289];
290
291const fn compute_offsets(index_bits: &[u32; 64]) -> [usize; 64] {
292 let mut offsets = [0usize; 64];
293 let mut total = 0usize;
294 let mut i = 0;
295 while i < 64 {
296 offsets[i] = total;
297 total += 1usize << index_bits[i];
298 i += 1;
299 }
300 offsets
301}
302
303const fn total_table_size(index_bits: &[u32; 64]) -> usize {
304 let mut total = 0usize;
305 let mut i = 0;
306 while i < 64 {
307 total += 1usize << index_bits[i];
308 i += 1;
309 }
310 total
311}
312
313const ROOK_OFFSETS: [usize; 64] = compute_offsets(&ROOK_INDEX_BITS);
314const BISHOP_OFFSETS: [usize; 64] = compute_offsets(&BISHOP_INDEX_BITS);
315const ROOK_TABLE_SIZE: usize = total_table_size(&ROOK_INDEX_BITS);
316const BISHOP_TABLE_SIZE: usize = total_table_size(&BISHOP_INDEX_BITS);
317
318pub(crate) const ROOK_DIRS: [(i8, i8); 4] = [(0, 1), (0, -1), (1, 0), (-1, 0)];
319pub(crate) const BISHOP_DIRS: [(i8, i8); 4] = [(1, 1), (1, -1), (-1, 1), (-1, -1)];
320
321#[derive(Debug, Clone, Copy)]
326pub(crate) struct MagicEntry {
327 pub mask: Bitboard,
328 pub magic: u64,
329 pub shift: u32,
331 pub offset: u32,
333}
334
335const fn compute_rook_entries() -> [MagicEntry; 64] {
337 let mut entries = [MagicEntry {
338 mask: Bitboard(0),
339 magic: 0,
340 shift: 0,
341 offset: 0,
342 }; 64];
343 let mut total: u32 = 0;
344 let mut i = 0;
345 while i < 64 {
346 entries[i] = MagicEntry {
347 mask: ROOK_MASKS[i],
348 magic: ROOK_MAGICS[i],
349 shift: 64 - ROOK_INDEX_BITS[i],
350 offset: total,
351 };
352 total += 1u32 << ROOK_INDEX_BITS[i];
353 i += 1;
354 }
355 entries
356}
357
358const fn compute_bishop_entries() -> [MagicEntry; 64] {
360 let mut entries = [MagicEntry {
361 mask: Bitboard(0),
362 magic: 0,
363 shift: 0,
364 offset: 0,
365 }; 64];
366 let mut total: u32 = 0;
367 let mut i = 0;
368 while i < 64 {
369 entries[i] = MagicEntry {
370 mask: BISHOP_MASKS[i],
371 magic: BISHOP_MAGICS[i],
372 shift: 64 - BISHOP_INDEX_BITS[i],
373 offset: total,
374 };
375 total += 1u32 << BISHOP_INDEX_BITS[i];
376 i += 1;
377 }
378 entries
379}
380
381pub(crate) const ROOK_ENTRIES: [MagicEntry; 64] = compute_rook_entries();
382pub(crate) const BISHOP_ENTRIES: [MagicEntry; 64] = compute_bishop_entries();
383
384pub(crate) fn sliding_attack(directions: &[(i8, i8)], sq: Square, occupied: Bitboard) -> Bitboard {
390 let mut result = 0u64;
391 let s_idx = sq as i8;
392 let sf = s_idx % 8;
393 let sr = s_idx / 8;
394
395 for &(df, dr) in directions {
396 let mut f = sf + df;
397 let mut r = sr + dr;
398 while (0..8).contains(&f) && (0..8).contains(&r) {
399 let idx = (r * 8 + f) as usize;
400 result |= 1u64 << idx;
401 if occupied.0 & (1u64 << idx) != 0 {
402 break;
403 }
404 f += df;
405 r += dr;
406 }
407 }
408 Bitboard(result)
409}
410
411fn build_magic_table(
417 directions: &[(i8, i8)],
418 masks: &[Bitboard; 64],
419 magics: &[u64; 64],
420 index_bits: &[u32; 64],
421 offsets: &[usize; 64],
422 total_size: usize,
423) -> Box<[Bitboard]> {
424 let mut table = vec![Bitboard::EMPTY; total_size].into_boxed_slice();
425
426 for sq in 0..64 {
427 let mask = masks[sq].0;
428 let magic = magics[sq];
429 let shift = 64 - index_bits[sq];
430 let offset = offsets[sq];
431 let size_check = 1usize << index_bits[sq];
432 let sq_enum = Square::from_u8(sq as u8);
433
434 let mut subset = 0u64;
436 let mut count = 0usize;
437 loop {
438 let attacks = sliding_attack(directions, sq_enum, Bitboard(subset));
439 let idx = (subset.wrapping_mul(magic) >> shift) as usize;
440 debug_assert!(
441 idx < size_check,
442 "index {} out of bounds for square {}",
443 idx,
444 sq
445 );
446 table[offset + idx] = attacks;
447 count += 1;
448
449 subset = subset.wrapping_sub(mask) & mask;
451 if subset == 0 {
452 break;
453 }
454 }
455
456 debug_assert_eq!(
457 count, size_check,
458 "wrong number of subsets for square {}",
459 sq
460 );
461 }
462
463 table
464}
465
466static ROOK_TABLE: OnceLock<&[Bitboard]> = OnceLock::new();
467static BISHOP_TABLE: OnceLock<&[Bitboard]> = OnceLock::new();
468
469pub(crate) fn init() {
471 _ = ROOK_TABLE.set(Box::leak(build_magic_table(
472 &ROOK_DIRS,
473 &ROOK_MASKS,
474 &ROOK_MAGICS,
475 &ROOK_INDEX_BITS,
476 &ROOK_OFFSETS,
477 ROOK_TABLE_SIZE,
478 )));
479 _ = BISHOP_TABLE.set(Box::leak(build_magic_table(
480 &BISHOP_DIRS,
481 &BISHOP_MASKS,
482 &BISHOP_MAGICS,
483 &BISHOP_INDEX_BITS,
484 &BISHOP_OFFSETS,
485 BISHOP_TABLE_SIZE,
486 )));
487}
488
489#[inline(always)]
491pub fn bishop_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
492 let table = BISHOP_TABLE
493 .get()
494 .expect("magic tables not initialized — call attacks::init()");
495 let e = &BISHOP_ENTRIES[sq as usize];
496 let idx = ((occupied & e.mask).0.wrapping_mul(e.magic)) >> e.shift;
497 table[e.offset as usize + idx as usize]
498}
499
500#[inline(always)]
502pub fn rook_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
503 let table = ROOK_TABLE
504 .get()
505 .expect("magic tables not initialized — call attacks::init()");
506 let e = &ROOK_ENTRIES[sq as usize];
507 let idx = ((occupied & e.mask).0.wrapping_mul(e.magic)) >> e.shift;
508 table[e.offset as usize + idx as usize]
509}
510
511#[inline(always)]
513pub fn queen_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
514 bishop_attacks(sq, occupied) | rook_attacks(sq, occupied)
515}
516
517#[cfg(test)]
518pub fn bishop_attacks_loop(sq: Square, occupied: Bitboard) -> Bitboard {
519 sliding_attack(&BISHOP_DIRS, sq, occupied)
520}
521
522#[cfg(test)]
523pub fn rook_attacks_loop(sq: Square, occupied: Bitboard) -> Bitboard {
524 sliding_attack(&ROOK_DIRS, sq, occupied)
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530
531 #[test]
534 fn test_magic_vs_loop_bishop() {
535 super::init();
536 for sq_idx in 0..64 {
537 let sq = Square::from_u8(sq_idx as u8);
538 let mask = BISHOP_MASKS[sq_idx];
539 let size = 1 << BISHOP_INDEX_BITS[sq_idx];
540 let mut count = 0;
541 let mut subset = 0u64;
542 loop {
543 let occ = Bitboard(subset);
544 let magic_atk = bishop_attacks(sq, occ);
545 let loop_atk = bishop_attacks_loop(sq, occ);
546 assert_eq!(
547 magic_atk, loop_atk,
548 "Bishop mismatch at sq={:?}, occ=0x{:x}",
549 sq, subset
550 );
551 count += 1;
552 subset = subset.wrapping_sub(mask.0) & mask.0;
553 if subset == 0 {
554 break;
555 }
556 }
557 assert_eq!(count, size, "Bishop count mismatch at sq={:?}", sq);
558 }
559 }
560
561 #[test]
562 fn test_magic_vs_loop_rook() {
563 super::init();
564 for sq_idx in 0..64 {
565 let sq = Square::from_u8(sq_idx as u8);
566 let mask = ROOK_MASKS[sq_idx];
567 let size = 1 << ROOK_INDEX_BITS[sq_idx];
568 let mut count = 0;
569 let mut subset = 0u64;
570 loop {
571 let occ = Bitboard(subset);
572 let magic_atk = rook_attacks(sq, occ);
573 let loop_atk = rook_attacks_loop(sq, occ);
574 assert_eq!(
575 magic_atk, loop_atk,
576 "Rook mismatch at sq={:?}, occ=0x{:x}",
577 sq, subset
578 );
579 count += 1;
580 subset = subset.wrapping_sub(mask.0) & mask.0;
581 if subset == 0 {
582 break;
583 }
584 }
585 assert_eq!(count, size, "Rook count mismatch at sq={:?}", sq);
586 }
587 }
588}