1use std::mem;
2use std::ptr;
3
4use core::masks::*;
5use SQ;
6
7use core::bit_twiddles::popcount64;
8use core::{file_bb, rank_bb};
9use tools::prng::PRNG;
10
11const ROOK_M_SIZE: usize = 102_400;
13static mut ROOK_MAGICS: [SMagic; 64] = [SMagic::init(); 64];
14static mut ROOK_TABLE: [u64; ROOK_M_SIZE] = [0; ROOK_M_SIZE];
15
16const BISHOP_M_SIZE: usize = 5248;
18static mut BISHOP_MAGICS: [SMagic; 64] = [SMagic::init(); 64];
19static mut BISHOP_TABLE: [u64; BISHOP_M_SIZE] = [0; BISHOP_M_SIZE];
20
21const B_DELTAS: [i8; 4] = [7, 9, -9, -7];
22const R_DELTAS: [i8; 4] = [8, 1, -8, -1];
23
24const SEEDS: [[u64; 8]; 2] = [
25 [8977, 44_560, 54_343, 38_998, 5731, 95_205, 104_912, 17_020],
26 [728, 10_316, 55_013, 32_803, 12_281, 15_100, 16_645, 255],
27];
28
29#[cold]
30pub fn init_magics() {
31 unsafe {
32 gen_magic_board(
33 BISHOP_M_SIZE,
34 &B_DELTAS,
35 BISHOP_MAGICS.as_mut_ptr(),
36 BISHOP_TABLE.as_mut_ptr(),
37 );
38 gen_magic_board(
39 ROOK_M_SIZE,
40 &R_DELTAS,
41 ROOK_MAGICS.as_mut_ptr(),
42 ROOK_TABLE.as_mut_ptr(),
43 );
44 }
45}
46
47#[inline]
48pub fn bishop_attacks(mut occupied: u64, square: u8) -> u64 {
49 let magic_entry: &SMagic = unsafe { BISHOP_MAGICS.get_unchecked(square as usize) };
50 occupied &= magic_entry.mask;
51 occupied = occupied.wrapping_mul(magic_entry.magic);
52 occupied = occupied.wrapping_shr(magic_entry.shift);
53 unsafe { *(magic_entry.ptr as *const u64).add(occupied as usize) }
54}
55
56#[inline]
57pub fn rook_attacks(mut occupied: u64, square: u8) -> u64 {
58 let magic_entry: &SMagic = unsafe { ROOK_MAGICS.get_unchecked(square as usize) };
59 occupied &= magic_entry.mask;
60 occupied = occupied.wrapping_mul(magic_entry.magic);
61 occupied = occupied.wrapping_shr(magic_entry.shift);
62 unsafe { *(magic_entry.ptr as *const u64).add(occupied as usize) }
63}
64
65#[derive(Copy, Clone)]
69struct SMagic {
70 ptr: usize,
71 mask: u64,
72 magic: u64,
73 shift: u32,
74}
75
76impl SMagic {
77 pub const fn init() -> Self {
78 SMagic {
79 ptr: 0,
80 mask: 0,
81 magic: 0,
82 shift: 0,
83 }
84 }
85}
86
87struct PreSMagic {
89 start: usize,
90 len: usize,
91 mask: u64,
92 magic: u64,
93 shift: u32,
94}
95
96impl PreSMagic {
97 pub fn init() -> PreSMagic {
98 PreSMagic {
99 start: 0,
100 len: 0,
101 mask: 0,
102 magic: 0,
103 shift: 0,
104 }
105 }
106
107 pub unsafe fn init64() -> [PreSMagic; 64] {
109 let arr: [PreSMagic; 64] = mem::MaybeUninit::uninit().assume_init();
110 arr
111 }
112
113 pub fn next_idx(&self) -> usize {
115 self.start + self.len
116 }
117}
118
119#[cold]
122unsafe fn gen_magic_board(
123 table_size: usize,
124 deltas: &[i8; 4],
125 static_magics: *mut SMagic,
126 attacks: *mut u64,
127) {
128 let mut pre_sq_table: [PreSMagic; 64] = PreSMagic::init64();
130
131 for table in pre_sq_table.iter_mut() {
133 *table = PreSMagic::init();
134 }
135
136 let mut occupancy: [u64; 4096] = [0; 4096];
140 let mut reference: [u64; 4096] = [0; 4096];
141 let mut age: [i32; 4096] = [0; 4096];
142
143 let mut size: usize;
145
146 let mut b: u64;
148
149 let mut current: i32 = 0;
151 let mut i: usize;
152
153 pre_sq_table[0].start = 0;
155
156 for s in 0..64 as u8 {
158 let mut magic: u64;
160
161 let edges: u64 = ((RANK_1 | RANK_8) & !rank_bb(s)) | ((FILE_A | FILE_H) & !file_bb(s));
165 let mask: u64 = sliding_attack(deltas, s, 0) & !edges;
166
167 let shift: u32 = (64 - popcount64(mask)) as u32;
169 b = 0;
170 size = 0;
171
172 'bit: loop {
174 occupancy[size] = b;
175 reference[size] = sliding_attack(deltas, s, b);
176 size += 1;
177 b = ((b).wrapping_sub(mask)) as u64 & mask;
178 if b == 0 {
179 break 'bit;
180 }
181 }
182
183 pre_sq_table[s as usize].len = size;
185
186 if s < 63 {
188 pre_sq_table[s as usize + 1].start = pre_sq_table[s as usize].next_idx();
189 }
190 let mut rng = PRNG::init(SEEDS[1][SQ(s).rank() as usize]);
192
193 'outer: loop {
195 'first_in: loop {
197 magic = rng.sparse_rand();
198 if popcount64((magic.wrapping_mul(mask)).wrapping_shr(56)) >= 6 {
199 break 'first_in;
200 }
201 }
202 current += 1;
203 i = 0;
204
205 while i < size {
207 let index: usize = ((occupancy[i as usize] & mask).wrapping_mul(magic) as u64)
209 .wrapping_shr(shift) as usize;
210
211 if age[index] < current {
213 age[index] = current;
216 *attacks.offset((pre_sq_table[s as usize].start + index) as isize) =
217 reference[i];
218 } else if *attacks.offset((pre_sq_table[s as usize].start + index) as isize)
219 != reference[i]
220 {
221 break;
223 }
224 i += 1;
225 }
226 if i >= size {
228 break 'outer;
229 }
230 }
231 pre_sq_table[s as usize].magic = magic;
233 pre_sq_table[s as usize].mask = mask;
234 pre_sq_table[s as usize].shift = shift;
235 }
236
237 let mut size = 0;
239 for i in 0..64 {
240 let beginptr = attacks.offset(size as isize);
242
243 let staticptr: *mut SMagic = static_magics.offset(i as isize);
245 let table_i: SMagic = SMagic {
246 ptr: mem::transmute::<*const u64, usize>(beginptr),
247 mask: pre_sq_table[i].mask,
248 magic: pre_sq_table[i].magic,
249 shift: pre_sq_table[i].shift,
250 };
251
252 ptr::copy::<SMagic>(&table_i, staticptr, 1);
253
254 size += pre_sq_table[i].len;
256 }
257 assert_eq!(size, table_size);
259}
260
261fn sliding_attack(deltas: &[i8; 4], sq: u8, occupied: u64) -> u64 {
265 assert!(sq < 64);
266 let mut attack: u64 = 0;
267 let square: i16 = sq as i16;
268 for delta in deltas.iter().take(4 as usize) {
269 let mut s: u8 = ((square as i16) + (*delta as i16)) as u8;
270 'inner: while s < 64 && SQ(s as u8).distance(SQ(((s as i16) - (*delta as i16)) as u8)) == 1
271 {
272 attack |= (1 as u64).wrapping_shl(s as u32);
273 if occupied & (1 as u64).wrapping_shl(s as u32) != 0 {
274 break 'inner;
275 }
276 s = ((s as i16) + (*delta as i16)) as u8;
277 }
278 }
279 attack
280}