1use core::{fmt, ops};
2
3use crate::{
4 Square,
5 square::{Direction, File, Rank},
6};
7
8use File::*;
9
10#[derive(Clone, Copy, Default, Eq, Ord, PartialEq, PartialOrd)]
11pub struct Bitboard(pub u64);
12
13impl Bitboard {
15 #[inline]
16 pub const fn from_file(file: File) -> Bitboard {
17 Bitboard(Bitboard::FILE_A.0 << file as u32)
18 }
19
20 #[inline]
21 pub const fn from_rank(rank: Rank) -> Bitboard {
22 Bitboard(Self::RANKS[rank as usize])
23 }
24
25 #[inline]
26 pub const fn from_square(square: Square) -> Bitboard {
27 Bitboard(1 << square as u32)
28 }
29
30 #[inline]
31 pub const fn from_squares<const N: usize>(squares: [Square; N]) -> Bitboard {
32 let mut bitboard = Bitboard::EMPTY;
33 let mut i = 0;
34 while i < N {
35 bitboard.append(Bitboard::from_square(squares[i]));
36 i += 1;
37 }
38 bitboard
39 }
40}
41
42impl Bitboard {
44 #[inline]
46 pub const fn append(&mut self, squares: Bitboard) {
47 self.0 |= squares.0;
48 }
49
50 #[inline]
51 pub const fn clear(&mut self) {
52 self.0 = 0;
53 }
54
55 #[inline]
56 pub const fn contains(self, square: Square) -> bool {
57 !self.intersection(Bitboard::from_square(square)).is_empty()
58 }
59
60 #[inline]
61 pub const fn difference(self, squares: Bitboard) -> Bitboard {
62 Bitboard(self.0 & !squares.0)
63 }
64
65 #[inline]
66 pub const fn eq(self, other: Self) -> bool {
67 self.0 == other.0
68 }
69
70 #[inline]
71 pub const fn first(self) -> Option<Square> {
72 if self.is_empty() {
73 None
74 } else {
75 Some(Square::panicky_from_index(self.0.trailing_zeros() as u8))
76 }
77 }
78
79 #[inline]
80 pub const fn insert(&mut self, square: Square) -> bool {
81 if self.contains(square) {
82 false
83 } else {
84 self.append(Bitboard::from_square(square));
85 true
86 }
87 }
88
89 #[inline]
90 pub const fn intersection(self, squares: Bitboard) -> Bitboard {
91 Bitboard(self.0 & squares.0)
92 }
93
94 #[inline]
95 pub const fn is_disjoint(self, other: Bitboard) -> bool {
96 self.intersection(other).is_empty()
97 }
98
99 #[inline]
100 pub const fn is_empty(self) -> bool {
101 self.0 == 0
102 }
103
104 #[inline]
105 pub const fn is_subset(self, other: Bitboard) -> bool {
106 self.difference(other).is_empty()
107 }
108
109 #[inline]
110 pub const fn is_superset(self, other: Bitboard) -> bool {
111 other.is_subset(self)
112 }
113
114 #[inline]
115 pub const fn iter(self) -> IntoIter {
116 IntoIter(self)
117 }
118
119 #[inline]
120 pub const fn last(self) -> Option<Square> {
121 if let Some(index) = self.0.checked_ilog2() {
122 Some(Square::panicky_from_index(index as u8))
123 } else {
124 None
125 }
126 }
127
128 #[inline]
129 pub const fn len(self) -> usize {
130 self.0.count_ones() as usize
131 }
132
133 #[inline]
134 pub const fn pop_first(&mut self) -> Option<Square> {
135 let square = self.first();
136 self.discard_first();
137 square
138 }
139
140 #[inline]
141 pub const fn pop_last(&mut self) -> Option<Square> {
142 let square = self.last();
143 self.discard_last();
144 square
145 }
146
147 #[inline]
148 pub const fn remove(&mut self, square: Square) -> bool {
149 if self.contains(square) {
150 *self = self.difference(Bitboard::from_square(square));
151 true
152 } else {
153 false
154 }
155 }
156
157 #[inline]
158 pub const fn symmetric_difference(self, squares: Bitboard) -> Bitboard {
159 Bitboard(self.0 ^ squares.0)
160 }
161
162 #[inline]
163 pub const fn union(self, squares: Bitboard) -> Bitboard {
164 Bitboard(self.0 | squares.0)
165 }
166
167 #[inline]
168 const fn discard_first(&mut self) {
169 *self = self.without_first();
170 }
171
172 #[inline]
173 const fn discard_last(&mut self) {
174 *self = self.without_last();
175 }
176
177 #[inline]
178 const fn without_last(self) -> Bitboard {
179 let Bitboard(mask) = self;
180 Bitboard(mask & !((1u64 << 63).wrapping_shr(mask.leading_zeros())))
181 }
182}
183
184impl Bitboard {
186 #[inline]
187 pub const fn more_than_one(self) -> bool {
188 !self.without_first().is_empty()
189 }
190
191 #[inline]
195 pub const fn next_subset(self, set: Bitboard) -> Bitboard {
196 Bitboard(set.0.wrapping_sub(self.0) & self.0)
218 }
219
220 #[inline]
221 pub const fn set(&mut self, square: Square, set: bool) {
223 if set {
224 self.append(Bitboard::from_square(square));
225 } else {
226 *self = self.difference(Bitboard::from_square(square));
227 }
228 }
229
230 #[inline]
231 pub const fn with(self, square: Square) -> Bitboard {
232 self.union(Bitboard::from_square(square))
233 }
234
235 #[inline]
236 pub const fn without_first(self) -> Bitboard {
237 let Bitboard(mask) = self;
238 Bitboard(mask & mask.wrapping_sub(1))
239 }
240}
241
242impl Bitboard {
244 #[inline]
245 pub const fn checked_shift(self, direction: Direction) -> Bitboard {
246 use Direction::*;
247
248 match direction {
249 North => self.wrapping_shift(North),
250 South => self.wrapping_shift(South),
251 East => self.without_h_file().wrapping_shift(East),
252 West => self.without_a_file().wrapping_shift(West),
253 NorthNorth => self.wrapping_shift(NorthNorth),
254 SouthSouth => self.wrapping_shift(SouthSouth),
255 NorthWest => self.without_a_file().wrapping_shift(NorthWest),
256 SouthWest => self.without_a_file().wrapping_shift(SouthWest),
257 NorthEast => self.without_h_file().wrapping_shift(NorthEast),
258 SouthEast => self.without_h_file().wrapping_shift(SouthEast),
259 KnightNorthWest => self.without_a_file().wrapping_shift(KnightNorthWest),
260 KnightSouthWest => self.without_a_file().wrapping_shift(KnightSouthWest),
261 KnightNorthEast => self.without_h_file().wrapping_shift(KnightNorthEast),
262 KnightSouthEast => self.without_h_file().wrapping_shift(KnightSouthEast),
263 KnightWestNorth => self.without_ab_files().wrapping_shift(KnightWestNorth),
264 KnightWestSouth => self.without_ab_files().wrapping_shift(KnightWestSouth),
265 KnightEastNorth => self.without_gh_files().wrapping_shift(KnightEastNorth),
266 KnightEastSouth => self.without_gh_files().wrapping_shift(KnightEastSouth),
267 }
268 }
269
270 #[inline]
271 pub const fn flip_anti_diagonal(self) -> Bitboard {
273 let k1 = 0xaa00_aa00_aa00_aa00;
275 let k2 = 0xcccc_0000_cccc_0000;
276 let k4 = 0xf0f0_f0f0_0f0f_0f0f;
277 let mut x = self.0;
278 let t = x ^ (x << 36);
279 x ^= k4 & (t ^ (x >> 36));
280 let t = k2 & (x ^ (x << 18));
281 x ^= t ^ (t >> 18);
282 let t = k1 & (x ^ (x << 9));
283 x ^= t ^ (t >> 9);
284 Bitboard(x)
285 }
286
287 #[inline]
288 pub const fn flip_diagonal(self) -> Bitboard {
290 let k1 = 0x5500_5500_5500_5500;
292 let k2 = 0x3333_0000_3333_0000;
293 let k4 = 0x0f0f_0f0f_0000_0000;
294 let mut x = self.0;
295 let t = k4 & (x ^ (x << 28));
296 x ^= t ^ (t >> 28);
297 let t = k2 & (x ^ (x << 14));
298 x ^= t ^ (t >> 14);
299 let t = k1 & (x ^ (x << 7));
300 x ^= t ^ (t >> 7);
301 Bitboard(x)
302 }
303
304 #[inline]
305 pub const fn flip_horizontal(self) -> Bitboard {
307 let k1 = 0x5555_5555_5555_5555;
309 let k2 = 0x3333_3333_3333_3333;
310 let k4 = 0x0f0f_0f0f_0f0f_0f0f;
311 let x = self.0;
312 let x = ((x >> 1) & k1) | ((x & k1) << 1);
313 let x = ((x >> 2) & k2) | ((x & k2) << 2);
314 let x = ((x >> 4) & k4) | ((x & k4) << 4);
315 Bitboard(x)
316 }
317
318 #[inline]
319 pub const fn flip_vertical(self) -> Bitboard {
321 Bitboard(self.0.swap_bytes())
322 }
323
324 #[inline]
325 pub const fn rotate_180(self) -> Bitboard {
327 Bitboard(self.0.reverse_bits())
328 }
329
330 #[inline]
331 pub const fn rotate_270(self) -> Bitboard {
333 self.flip_vertical().flip_diagonal()
334 }
335
336 #[inline]
337 pub const fn rotate_90(self) -> Bitboard {
339 self.flip_diagonal().flip_vertical()
340 }
341
342 #[inline]
343 pub const fn without_a_file(self) -> Bitboard {
344 self.difference(Bitboard::FILE_A)
345 }
346
347 #[inline]
348 pub const fn without_ab_files(self) -> Bitboard {
349 self.without_a_file().difference(Bitboard::from_file(B))
350 }
351
352 #[inline]
353 pub const fn without_gh_files(self) -> Bitboard {
354 self.without_h_file().difference(Bitboard::from_file(G))
355 }
356
357 #[inline]
358 pub const fn without_h_file(self) -> Bitboard {
359 self.difference(Bitboard::from_file(H))
360 }
361
362 #[inline]
363 const fn wrapping_shift(self, direction: Direction) -> Bitboard {
364 let offset = direction as i8;
365 if offset >= 0 {
366 Bitboard(self.0 << offset as u32)
367 } else {
368 Bitboard(self.0 >> -offset as u32)
369 }
370 }
371}
372
373impl fmt::Debug for Bitboard {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 use fmt::Write as _;
376
377 for rank in Rank::iter_rev() {
378 for file in File::iter() {
379 let square = Square::new(file, rank);
380 f.write_char(if self.contains(square) { '1' } else { '.' })?;
381 f.write_char(if file < H { ' ' } else { '\n' })?;
382 }
383 }
384
385 Ok(())
386 }
387}
388
389impl fmt::UpperHex for Bitboard {
390 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
391 fmt::UpperHex::fmt(&self.0, f)
392 }
393}
394
395impl fmt::LowerHex for Bitboard {
396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 fmt::LowerHex::fmt(&self.0, f)
398 }
399}
400
401impl fmt::Octal for Bitboard {
402 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
403 fmt::Octal::fmt(&self.0, f)
404 }
405}
406
407impl fmt::Binary for Bitboard {
408 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
409 fmt::Binary::fmt(&self.0, f)
410 }
411}
412
413impl From<Square> for Bitboard {
414 #[inline]
415 fn from(sq: Square) -> Bitboard {
416 Bitboard::from_square(sq)
417 }
418}
419
420impl From<Rank> for Bitboard {
421 #[inline]
422 fn from(rank: Rank) -> Bitboard {
423 Bitboard::from_rank(rank)
424 }
425}
426
427impl From<File> for Bitboard {
428 #[inline]
429 fn from(file: File) -> Bitboard {
430 Bitboard::from_file(file)
431 }
432}
433
434impl From<u64> for Bitboard {
435 #[inline]
436 fn from(bitboard: u64) -> Bitboard {
437 Bitboard(bitboard)
438 }
439}
440
441impl From<Bitboard> for u64 {
442 #[inline]
443 fn from(bitboard: Bitboard) -> u64 {
444 bitboard.0
445 }
446}
447
448impl<T> ops::BitAnd<T> for Bitboard
449where
450 T: Into<Bitboard>,
451{
452 type Output = Bitboard;
453
454 #[inline]
455 fn bitand(self, rhs: T) -> Bitboard {
456 let Bitboard(rhs) = rhs.into();
457 Bitboard(self.0 & rhs)
458 }
459}
460
461impl<T> ops::BitAndAssign<T> for Bitboard
462where
463 T: Into<Bitboard>,
464{
465 #[inline]
466 fn bitand_assign(&mut self, rhs: T) {
467 let Bitboard(rhs) = rhs.into();
468 self.0 &= rhs;
469 }
470}
471
472impl<T> ops::BitOr<T> for Bitboard
473where
474 T: Into<Bitboard>,
475{
476 type Output = Bitboard;
477
478 #[inline]
479 fn bitor(self, rhs: T) -> Bitboard {
480 let Bitboard(rhs) = rhs.into();
481 Bitboard(self.0 | rhs)
482 }
483}
484
485impl<T> ops::BitOrAssign<T> for Bitboard
486where
487 T: Into<Bitboard>,
488{
489 #[inline]
490 fn bitor_assign(&mut self, rhs: T) {
491 let Bitboard(rhs) = rhs.into();
492 self.0 |= rhs;
493 }
494}
495
496impl<T> ops::BitXor<T> for Bitboard
497where
498 T: Into<Bitboard>,
499{
500 type Output = Bitboard;
501
502 #[inline]
503 fn bitxor(self, rhs: T) -> Bitboard {
504 let Bitboard(rhs) = rhs.into();
505 Bitboard(self.0 ^ rhs)
506 }
507}
508
509impl<T> ops::BitXorAssign<T> for Bitboard
510where
511 T: Into<Bitboard>,
512{
513 #[inline]
514 fn bitxor_assign(&mut self, rhs: T) {
515 let Bitboard(rhs) = rhs.into();
516 self.0 ^= rhs;
517 }
518}
519
520impl ops::Not for Bitboard {
521 type Output = Bitboard;
522
523 #[inline]
524 fn not(self) -> Bitboard {
525 Bitboard(!self.0)
526 }
527}
528
529impl FromIterator<Square> for Bitboard {
530 fn from_iter<T>(iter: T) -> Bitboard
531 where
532 T: IntoIterator<Item = Square>,
533 {
534 let mut bitboard = Bitboard(0);
535 bitboard.extend(iter);
536 bitboard
537 }
538}
539
540impl Extend<Square> for Bitboard {
541 fn extend<T: IntoIterator<Item = Square>>(&mut self, iter: T) {
542 for square in iter {
543 self.insert(square);
544 }
545 }
546}
547
548impl IntoIterator for Bitboard {
549 type Item = Square;
550 type IntoIter = IntoIter;
551
552 #[inline]
553 fn into_iter(self) -> IntoIter {
554 IntoIter(self)
555 }
556}
557
558#[derive(Clone, Debug, Default)]
560pub struct IntoIter(Bitboard);
561
562impl Iterator for IntoIter {
563 type Item = Square;
564
565 #[inline]
566 fn next(&mut self) -> Option<Square> {
567 self.0.pop_first()
568 }
569
570 #[inline]
571 fn count(self) -> usize {
572 self.0.len()
573 }
574
575 #[inline]
576 fn size_hint(&self) -> (usize, Option<usize>) {
577 let len = self.0.len();
578 (len, Some(len))
579 }
580
581 #[inline]
582 fn last(self) -> Option<Square> {
583 self.0.last()
584 }
585
586 #[inline]
587 fn fold<B, F>(self, init: B, mut f: F) -> B
588 where
589 F: FnMut(B, Square) -> B,
590 {
591 let mut accum = init;
592 let Bitboard(mut mask) = self.0;
593 while mask != 0 {
594 accum = f(accum, Square::panicky_from_index(mask.trailing_zeros() as u8));
595 mask = mask & mask.wrapping_sub(1);
596 }
597 accum
598 }
599}
600
601impl ExactSizeIterator for IntoIter {
602 #[inline]
603 fn len(&self) -> usize {
604 self.0.len()
605 }
606}
607
608impl DoubleEndedIterator for IntoIter {
609 #[inline]
610 fn next_back(&mut self) -> Option<Square> {
611 self.0.pop_last()
612 }
613}
614
615impl core::iter::FusedIterator for IntoIter {}
616
617impl Bitboard {
619 pub const ANTIDIAGONAL: Bitboard = Bitboard(0x0102_0408_1020_4080);
621 pub const BACKRANKS: Bitboard = Bitboard(0xff00_0000_0000_00ff);
622 pub const BLACK: Bitboard = Bitboard(0xaa55_aa55_aa55_aa55);
623 pub const CENTER: Bitboard = Bitboard(0x0000_0018_1800_0000);
624 pub const CORNERS: Bitboard = Bitboard(0x8100_0000_0000_0081);
625 pub const DIAGONAL: Bitboard = Bitboard(0x8040_2010_0804_0201);
627 pub const EAST: Bitboard = Bitboard(0xf0f0_f0f0_f0f0_f0f0);
628 pub const EMPTY: Bitboard = Bitboard(0);
629 pub const FILE_A: Bitboard = Bitboard(0x0101_0101_0101_0101);
630 pub const FULL: Bitboard = Bitboard(!0);
631 pub const FULL_RAYS: [[Bitboard; 64]; 64] = {
632 let mut table = [[Bitboard::EMPTY; 64]; 64];
633 let mut a = 0i32;
634 while a < 64 {
635 let file_a = a & 7;
636 let rank_a = a >> 3;
637 let diagonal_a = rank_a - file_a;
638 let antidiagonal_a = rank_a + file_a - 7;
639
640 let mut b = 0i32;
641 while b < 64 {
642 let file_b = b & 7;
643 let rank_b = b >> 3;
644 let diagonal_b = rank_b - file_b;
645 let antidiagonal_b = rank_b + file_b - 7;
646
647 table[a as usize][b as usize] = Bitboard(if a == b {
648 0
649 } else if file_a == file_b {
650 Bitboard::FILE_A.0 << file_a
651 } else if rank_a == rank_b {
652 0xff << (8 * rank_a)
653 } else if diagonal_a == diagonal_b {
654 if diagonal_a >= 0 {
655 Bitboard::DIAGONAL.0 << (8 * diagonal_a)
656 } else {
657 Bitboard::DIAGONAL.0 >> (8 * -diagonal_a)
658 }
659 } else if antidiagonal_a == antidiagonal_b {
660 if antidiagonal_a >= 0 {
661 Bitboard::ANTIDIAGONAL.0 << (8 * antidiagonal_a)
662 } else {
663 Bitboard::ANTIDIAGONAL.0 >> (8 * -antidiagonal_a)
664 }
665 } else {
666 0
667 });
668 b += 1;
669 }
670 a += 1;
671 }
672 table
673 };
674 pub const NORTH: Bitboard = Bitboard(0xffff_ffff_0000_0000);
675 const RANKS: [u64; 8] = {
676 let mut masks = [0; 8];
677 let mut i = 0;
678 while i < 8 {
679 masks[i] = 0xff << (i * 8);
680 i += 1;
681 }
682 masks
683 };
684 pub const SOUTH: Bitboard = Bitboard(0x0000_0000_ffff_ffff);
685 pub const WEST: Bitboard = Bitboard(0x0f0f_0f0f_0f0f_0f0f);
686 pub const WHITE: Bitboard = Bitboard(0x55aa_55aa_55aa_55aa);
687}
688
689#[cfg(test)]
690mod tests {
691 use super::*;
692
693 #[test]
694 fn bitboard_constants() {
695 println!("{:?}", Bitboard::NORTH);
696 println!("{:?}", Bitboard::EAST);
697 println!("{:?}", Bitboard::SOUTH);
698 println!("{:?}", Bitboard::WEST);
699 }
700}