1use super::*;
2
3#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
4#[repr(u8)]
5#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
6pub enum CastleRights {
7 None = 0,
8 KingSide = 1,
9 QueenSide = 2,
10 Both = 3,
11}
12
13impl CastleRights {
14 #[inline]
16 pub const fn has_kingside(self) -> bool {
17 self.to_index() & 1 == 1
18 }
19
20 #[inline]
22 pub const fn has_queenside(self) -> bool {
23 self.to_index() & 2 == 2
24 }
25
26 #[inline]
27 pub fn square_to_castle_rights(color: Color, square: Square) -> Self {
28 *get_item_unchecked!(
29 const {
30 let mut array = [[Self::None; 64]; 2];
31 array[0][63] = Self::KingSide;
32 array[1][7] = Self::KingSide;
33 array[0][56] = Self::QueenSide;
34 array[1][0] = Self::QueenSide;
35 array[0][60] = Self::Both;
36 array[1][4] = Self::Both;
37 array
38 },
39 color.to_index(),
40 square.to_index()
41 )
42 }
43
44 #[inline]
46 pub fn kingside_squares(self, color: Color) -> BitBoard {
47 *get_item_unchecked!(
48 const { [BitBoard::new(6917529027641081856), BitBoard::new(96)] },
49 color.to_index(),
50 )
51 }
52
53 #[inline]
55 pub fn queenside_squares(self, color: Color) -> BitBoard {
56 *get_item_unchecked!(
57 const { [BitBoard::new(1008806316530991104), BitBoard::new(14)] },
58 color.to_index(),
59 )
60 }
61
62 #[inline]
64 pub fn remove(self, remove: Self) -> Self {
65 unsafe { Self::from_int(self.to_int() & !remove.to_int()) }
66 }
67
68 #[inline]
70 pub const fn to_int(self) -> u8 {
71 self as u8
72 }
73
74 #[inline]
76 pub const fn to_index(self) -> usize {
77 self as usize
78 }
79
80 #[inline]
82 pub const unsafe fn from_int(i: u8) -> Self {
83 std::mem::transmute(i)
84 }
85
86 #[inline]
88 pub const unsafe fn from_index(i: usize) -> Self {
89 Self::from_int(i as u8)
90 }
91
92 #[inline]
94 pub fn unmoved_rooks(self, color: Color) -> BitBoard {
95 match self {
97 Self::None => BitBoard::EMPTY,
98 Self::KingSide => BitBoard::from_rank_and_file(color.to_my_backrank(), File::H),
99 Self::QueenSide => BitBoard::from_rank_and_file(color.to_my_backrank(), File::A),
100 Self::Both => {
101 let my_backrank = color.to_my_backrank();
102 BitBoard::from_rank_and_file(my_backrank, File::A)
103 ^ BitBoard::from_rank_and_file(my_backrank, File::H)
104 }
105 }
106 }
107
108 #[inline]
109 pub fn to_string(self, color: Color) -> &'static str {
110 get_item_unchecked!(
111 const { ["", "k", "q", "kq", "", "K", "Q", "KQ"] },
112 color.to_index() << 2 | self.to_index()
113 )
114 }
115}
116
117impl Add for CastleRights {
118 type Output = Self;
119
120 #[expect(clippy::suspicious_arithmetic_impl)]
121 #[inline]
122 fn add(self, rhs: Self) -> Self::Output {
123 unsafe { Self::from_int(self.to_int() | rhs.to_int()) }
124 }
125}
126
127impl AddAssign for CastleRights {
128 #[inline]
129 fn add_assign(&mut self, rhs: Self) {
130 *self = *self + rhs;
131 }
132}
133
134impl Sub for CastleRights {
135 type Output = Self;
136
137 #[inline]
138 fn sub(self, rhs: Self) -> Self::Output {
139 self.remove(rhs)
140 }
141}
142
143impl SubAssign for CastleRights {
144 #[inline]
145 fn sub_assign(&mut self, rhs: Self) {
146 *self = *self - rhs;
147 }
148}
149
150impl TryFrom<char> for CastleRights {
151 type Error = TimecatError;
152
153 fn try_from(value: char) -> Result<Self> {
154 match value {
155 'K' | 'k' => Ok(Self::KingSide),
156 'Q' | 'q' => Ok(Self::QueenSide),
157 _ => Err(TimecatError::InvalidCastleRightsString {
158 s: value.to_string().into(),
159 }),
160 }
161 }
162}
163
164impl FromStr for CastleRights {
165 type Err = TimecatError;
166
167 fn from_str(s: &str) -> Result<Self> {
168 match s.trim() {
169 "" => Ok(Self::None),
170 "K" | "k" => Ok(Self::KingSide),
171 "Q" | "q" => Ok(Self::QueenSide),
172 "KQ" | "kq" | "QK" | "qk" => Ok(Self::Both),
173 _ => Err(TimecatError::InvalidCastleRightsString {
174 s: s.to_string().into(),
175 }),
176 }
177 }
178}