atomic_movegen/
attacks.rs1use crate::types::*;
2
3#[cfg(target_arch = "x86_64")]
7mod sliding_dispatch {
8 use crate::types::*;
9 use core::sync::atomic::{AtomicU8, Ordering};
10
11 static IMPL: AtomicU8 = AtomicU8::new(0);
13
14 pub(crate) fn force_magic() {
15 IMPL.store(1, Ordering::Relaxed);
16 }
17
18 pub(crate) fn force_pext() {
19 IMPL.store(2, Ordering::Relaxed);
20 }
21
22 #[inline(always)]
23 pub fn bishop_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
24 if IMPL.load(Ordering::Relaxed) == 2 {
26 crate::pext::bishop_attacks_pext(sq, occupied)
27 } else {
28 crate::magic::bishop_attacks(sq, occupied)
29 }
30 }
31
32 #[inline(always)]
33 pub fn rook_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
34 if IMPL.load(Ordering::Relaxed) == 2 {
35 crate::pext::rook_attacks_pext(sq, occupied)
36 } else {
37 crate::magic::rook_attacks(sq, occupied)
38 }
39 }
40
41 #[inline(always)]
42 pub fn queen_attacks(sq: Square, occupied: Bitboard) -> Bitboard {
43 bishop_attacks(sq, occupied) | rook_attacks(sq, occupied)
44 }
45}
46
47#[cfg(target_arch = "x86_64")]
48pub use sliding_dispatch::{bishop_attacks, queen_attacks, rook_attacks};
49
50#[cfg(not(target_arch = "x86_64"))]
51pub use crate::magic::{bishop_attacks, queen_attacks, rook_attacks};
52
53const fn compute_king_attacks() -> [Bitboard; 64] {
57 let mut attacks = [Bitboard(0); 64];
58 let mut sq: u8 = 0;
59 while sq < 64 {
60 let f = sq % 8;
61 let r = sq / 8;
62 let mut atk = 0u64;
63
64 if r < 7 {
66 atk |= 1u64 << ((r + 1) * 8 + f);
67 }
68 if r > 0 {
70 atk |= 1u64 << ((r - 1) * 8 + f);
71 }
72 if f < 7 {
74 atk |= 1u64 << (r * 8 + f + 1);
75 }
76 if f > 0 {
78 atk |= 1u64 << (r * 8 + f - 1);
79 }
80 if r < 7 && f < 7 {
82 atk |= 1u64 << ((r + 1) * 8 + f + 1);
83 }
84 if r < 7 && f > 0 {
86 atk |= 1u64 << ((r + 1) * 8 + f - 1);
87 }
88 if r > 0 && f < 7 {
90 atk |= 1u64 << ((r - 1) * 8 + f + 1);
91 }
92 if r > 0 && f > 0 {
94 atk |= 1u64 << ((r - 1) * 8 + f - 1);
95 }
96
97 attacks[sq as usize] = Bitboard(atk);
98 sq += 1;
99 }
100 attacks
101}
102
103const fn compute_knight_attacks() -> [Bitboard; 64] {
105 let mut attacks = [Bitboard(0); 64];
106 let mut sq: u8 = 0;
107 while sq < 64 {
108 let f = sq as i8 % 8;
109 let r = sq as i8 / 8;
110 let mut atk = 0u64;
111 let offsets: [i8; 8] = [17, 15, 10, 6, -6, -10, -15, -17];
113 let mut i: u8 = 0;
114 while i < 8 {
115 let to = sq as i8 + offsets[i as usize];
116 if to >= 0 && to < 64 {
117 let tf = to % 8;
118 let tr = to / 8;
119 let df = tf - f;
120 let dr = tr - r;
121 if (df == 1 || df == -1) && (dr == 2 || dr == -2)
124 || (df == 2 || df == -2) && (dr == 1 || dr == -1)
125 {
126 atk |= 1u64 << to;
127 }
128 }
129 i += 1;
130 }
131 attacks[sq as usize] = Bitboard(atk);
132 sq += 1;
133 }
134 attacks
135}
136
137const fn compute_pawn_attacks() -> [[Bitboard; 2]; 64] {
140 let mut attacks = [[Bitboard(0); 2]; 64];
141 let mut sq: u8 = 0;
142 while sq < 64 {
143 let f = sq % 8;
144 let r = sq / 8;
145 let mut white_atk = 0u64;
146 let mut black_atk = 0u64;
147
148 if r < 7 {
150 if f > 0 {
151 white_atk |= 1u64 << ((r + 1) * 8 + f - 1);
152 }
153 if f < 7 {
154 white_atk |= 1u64 << ((r + 1) * 8 + f + 1);
155 }
156 }
157 if r > 0 {
159 if f > 0 {
160 black_atk |= 1u64 << ((r - 1) * 8 + f - 1);
161 }
162 if f < 7 {
163 black_atk |= 1u64 << ((r - 1) * 8 + f + 1);
164 }
165 }
166
167 attacks[sq as usize] = [Bitboard(white_atk), Bitboard(black_atk)];
168 sq += 1;
169 }
170 attacks
171}
172
173const KING_ATTACKS: [Bitboard; 64] = compute_king_attacks();
175
176const KNIGHT_ATTACKS: [Bitboard; 64] = compute_knight_attacks();
178
179const PAWN_ATTACKS: [[Bitboard; 2]; 64] = compute_pawn_attacks();
181
182const fn compute_between_bb() -> [[Bitboard; 64]; 64] {
184 let mut table = [[Bitboard(0); 64]; 64];
185 let mut s1: u8 = 0;
186 while s1 < 64 {
187 let mut s2: u8 = 0;
188 while s2 < 64 {
189 let f1 = s1 % 8;
190 let r1 = s1 / 8;
191 let f2 = s2 % 8;
192 let r2 = s2 / 8;
193
194 if s1 != s2
195 && (f1 == f2
196 || r1 == r2
197 || (f1 as i8 - f2 as i8).abs() == (r1 as i8 - r2 as i8).abs())
198 {
199 let mut b = 0u64;
200 let df = (f2 as i8 - f1 as i8).signum();
201 let dr = (r2 as i8 - r1 as i8).signum();
202 let mut f = f1 as i8 + df;
203 let mut r = r1 as i8 + dr;
204 while f != f2 as i8 || r != r2 as i8 {
205 b |= 1u64 << ((r as u8) * 8 + (f as u8));
206 f += df;
207 r += dr;
208 }
209 table[s1 as usize][s2 as usize] = Bitboard(b);
210 }
211 s2 += 1;
212 }
213 s1 += 1;
214 }
215 table
216}
217
218const fn compute_line_bb() -> [[Bitboard; 64]; 64] {
220 let mut table = [[Bitboard(0); 64]; 64];
221 let mut s1: u8 = 0;
222 while s1 < 64 {
223 let mut s2: u8 = 0;
224 while s2 < 64 {
225 let f1 = s1 % 8;
226 let r1 = s1 / 8;
227 let f2 = s2 % 8;
228 let r2 = s2 / 8;
229
230 if s1 != s2
231 && (f1 == f2
232 || r1 == r2
233 || (f1 as i8 - f2 as i8).abs() == (r1 as i8 - r2 as i8).abs())
234 {
235 let mut b = 0u64;
236 let df = (f2 as i8 - f1 as i8).signum();
237 let dr = (r2 as i8 - r1 as i8).signum();
238 let mut f = f1 as i8;
239 let mut r = r1 as i8;
240 while f >= 0 && f < 8 && r >= 0 && r < 8 {
241 b |= 1u64 << ((r as u8) * 8 + (f as u8));
242 f += df;
243 r += dr;
244 }
245 table[s1 as usize][s2 as usize] = Bitboard(b);
246 }
247 s2 += 1;
248 }
249 s1 += 1;
250 }
251 table
252}
253
254pub(crate) static BETWEEN_BB: [[Bitboard; 64]; 64] = compute_between_bb();
258
259pub(crate) static LINE_BB: [[Bitboard; 64]; 64] = compute_line_bb();
264
265#[inline(always)]
267pub fn king_attacks(sq: Square) -> Bitboard {
268 KING_ATTACKS[sq as usize]
269}
270
271#[inline(always)]
273pub fn knight_attacks(sq: Square) -> Bitboard {
274 KNIGHT_ATTACKS[sq as usize]
275}
276
277#[inline(always)]
279pub fn pawn_attacks(c: Color, sq: Square) -> Bitboard {
280 PAWN_ATTACKS[sq as usize][c as usize]
281}
282
283pub fn init() {
288 crate::magic::init();
289 #[cfg(target_arch = "x86_64")]
290 {
291 if crate::pext::has_bmi2() {
292 crate::pext::init();
293 sliding_dispatch::force_pext();
294 } else {
295 sliding_dispatch::force_magic();
296 }
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_knight_attacks_center() {
306 let atk = knight_attacks(Square::D4);
307 assert!(atk & Bitboard::square_bb(Square::C2) != Bitboard::EMPTY);
308 assert!(atk & Bitboard::square_bb(Square::E2) != Bitboard::EMPTY);
309 assert!(atk & Bitboard::square_bb(Square::B3) != Bitboard::EMPTY);
310 assert!(atk & Bitboard::square_bb(Square::F3) != Bitboard::EMPTY);
311 assert!(atk & Bitboard::square_bb(Square::B5) != Bitboard::EMPTY);
312 assert!(atk & Bitboard::square_bb(Square::F5) != Bitboard::EMPTY);
313 assert!(atk & Bitboard::square_bb(Square::C6) != Bitboard::EMPTY);
314 assert!(atk & Bitboard::square_bb(Square::E6) != Bitboard::EMPTY);
315 assert_eq!(atk.count(), 8);
316 }
317
318 #[test]
319 fn test_knight_attacks_corner() {
320 let atk = knight_attacks(Square::A1);
321 assert_eq!(atk.count(), 2);
322 assert!(atk & Bitboard::square_bb(Square::B3) != Bitboard::EMPTY);
323 assert!(atk & Bitboard::square_bb(Square::C2) != Bitboard::EMPTY);
324 }
325
326 #[test]
327 fn test_king_attacks_center() {
328 let atk = king_attacks(Square::D4);
329 assert_eq!(atk.count(), 8);
330 }
331
332 #[test]
333 fn test_king_attacks_corner() {
334 let atk = king_attacks(Square::A1);
335 assert_eq!(atk.count(), 3);
336 }
337
338 #[test]
339 fn test_bishop_attacks() {
340 crate::attacks::init();
341 let atk = bishop_attacks(Square::D4, Bitboard::EMPTY);
342 assert!(atk & Bitboard::square_bb(Square::A1) != Bitboard::EMPTY);
343 assert!(atk & Bitboard::square_bb(Square::G7) != Bitboard::EMPTY);
344 assert!((atk & Bitboard::square_bb(Square::D4)).is_empty());
345 }
346
347 #[test]
348 fn test_rook_attacks() {
349 crate::attacks::init();
350 let atk = rook_attacks(Square::D4, Bitboard::EMPTY);
351 assert!(atk & Bitboard::square_bb(Square::D1) != Bitboard::EMPTY);
352 assert!(atk & Bitboard::square_bb(Square::D8) != Bitboard::EMPTY);
353 assert!(atk & Bitboard::square_bb(Square::A4) != Bitboard::EMPTY);
354 assert!(atk & Bitboard::square_bb(Square::H4) != Bitboard::EMPTY);
355 }
356
357 #[test]
358 fn test_bishop_blocked() {
359 crate::attacks::init();
360 let blocker = Bitboard::square_bb(Square::E5);
361 let atk = bishop_attacks(Square::D4, blocker);
362 assert!(
363 atk & Bitboard::square_bb(Square::E5) != Bitboard::EMPTY,
364 "blocker square should be attackable"
365 );
366 assert!(
367 (atk & Bitboard::square_bb(Square::F6)).is_empty(),
368 "beyond blocker should be blocked"
369 );
370 }
371
372 #[test]
373 fn test_rook_blocked() {
374 crate::attacks::init();
375 let blocker = Bitboard::square_bb(Square::D5);
376 let atk = rook_attacks(Square::D4, blocker);
377 assert!(atk & Bitboard::square_bb(Square::D5) != Bitboard::EMPTY);
378 assert!((atk & Bitboard::square_bb(Square::D6)).is_empty());
379 }
380
381 #[test]
382 fn test_queen_equals_bishop_plus_rook() {
383 crate::attacks::init();
384 let occ = Bitboard::square_bb(Square::E5);
385 let queen = queen_attacks(Square::D4, occ);
386 let bishop = bishop_attacks(Square::D4, occ);
387 let rook = rook_attacks(Square::D4, occ);
388 assert_eq!(queen, bishop | rook);
389 }
390}