1use crate::constants::NUM_PLANES;
2
3#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
8#[repr(C, align(16))]
9pub struct Bitboard {
10 pub planes: [u16; NUM_PLANES],
11}
12
13impl Bitboard {
14 pub const EMPTY: Self = Self {
15 planes: [0; NUM_PLANES],
16 };
17
18 #[inline]
19 pub fn new(planes: [u16; NUM_PLANES]) -> Self {
20 Self { planes }
21 }
22
23 #[inline]
24 pub fn plane_index(player: u8, shape: u8) -> usize {
25 (player as usize) * 4 + (shape as usize)
26 }
27
28 #[inline]
29 pub fn occupied(&self) -> u16 {
30 self.planes.iter().fold(0u16, |acc, &p| acc | p)
31 }
32
33 #[inline]
34 pub fn is_position_occupied(&self, pos: u8) -> bool {
35 self.occupied() & (1u16 << pos) != 0
36 }
37
38 #[inline]
40 pub fn with_move(&self, player: u8, shape: u8, position: u8) -> Self {
41 let mut bb = *self;
42 bb.planes[Self::plane_index(player, shape)] |= 1u16 << position;
43 bb
44 }
45
46 #[inline]
48 pub fn player_piece_count(&self, player: u8) -> u32 {
49 let base = (player as usize) * 4;
50 (0..4).map(|s| self.planes[base + s].count_ones()).sum()
51 }
52
53 #[inline]
55 pub fn shape_piece_count(&self, player: u8, shape: u8) -> u32 {
56 self.planes[Self::plane_index(player, shape)].count_ones()
57 }
58
59 pub fn to_le_bytes(&self) -> [u8; 16] {
61 let mut buf = [0u8; 16];
62 for (i, &plane) in self.planes.iter().enumerate() {
63 let bytes = plane.to_le_bytes();
64 buf[i * 2] = bytes[0];
65 buf[i * 2 + 1] = bytes[1];
66 }
67 buf
68 }
69
70 pub fn from_le_bytes(buf: &[u8; 16]) -> Self {
72 let mut planes = [0u16; NUM_PLANES];
73 for i in 0..NUM_PLANES {
74 planes[i] = u16::from_le_bytes([buf[i * 2], buf[i * 2 + 1]]);
75 }
76 Self { planes }
77 }
78}
79
80impl Default for Bitboard {
81 fn default() -> Self {
82 Self::EMPTY
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn empty_bitboard() {
92 let bb = Bitboard::EMPTY;
93 assert_eq!(bb.occupied(), 0);
94 assert_eq!(bb.player_piece_count(0), 0);
95 assert_eq!(bb.player_piece_count(1), 0);
96 }
97
98 #[test]
99 fn with_move_sets_bit() {
100 let bb = Bitboard::EMPTY.with_move(0, 0, 5);
101 assert!(bb.is_position_occupied(5));
102 assert!(!bb.is_position_occupied(0));
103 assert_eq!(bb.shape_piece_count(0, 0), 1);
104 }
105
106 #[test]
107 fn roundtrip_bytes() {
108 let bb = Bitboard::new([1, 2, 3, 4, 5, 6, 7, 8]);
109 let bytes = bb.to_le_bytes();
110 let bb2 = Bitboard::from_le_bytes(&bytes);
111 assert_eq!(bb, bb2);
112 }
113
114 #[test]
115 fn plane_index_mapping() {
116 assert_eq!(Bitboard::plane_index(0, 0), 0);
117 assert_eq!(Bitboard::plane_index(0, 3), 3);
118 assert_eq!(Bitboard::plane_index(1, 0), 4);
119 assert_eq!(Bitboard::plane_index(1, 3), 7);
120 }
121}