1use core::str::FromStr;
2
3use crate::{
4 Side, Square,
5 square::{Direction, File, Rank},
6};
7
8use super::Bitboard;
9
10use Player::*;
11use Rank::*;
12use Role::*;
13
14#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
16pub struct Piece {
17 pub player: Player,
18 pub role: Role,
19}
20
21crate::finite_set!(
22 #[derive(DeserializeFromStr, SerializeDisplay)]
24 Player,
25 Players,
26 PlayerTable,
27 {
28 Black = 0 as black,
29 White = 1 as white,
30 }
31);
32
33crate::finite_set!(
34 Role,
36 Roles,
37 RoleTable,
38 {
39 Pawn = 1 as pawn,
40 Knight = 2 as knight,
41 Bishop = 3 as bishop,
42 Rook = 5 as rook,
43 Queen = 9 as queen,
44 King = 4 as king,
45 }
46);
47
48impl Piece {
49 #[inline]
50 pub const fn from_char(c: char) -> Option<Self> {
51 let player = if c.is_ascii_lowercase() { Black } else { White };
52 match Role::from_char(c) {
53 Some(role) => Some(Self { player, role }),
54 None => None,
55 }
56 }
57
58 #[inline]
59 #[track_caller]
60 pub(crate) const fn panicky_from_char(c: char) -> Self {
61 match Self::from_char(c) {
62 Some(piece) => piece,
63 None => panic!("invalid piece character"),
64 }
65 }
66
67 #[inline]
68 pub const fn char(self) -> char {
69 match self.player {
70 Black => self.role.black(),
71 White => self.role.white(),
72 }
73 }
74
75 #[inline]
77 pub const fn eq(self, other: Piece) -> bool {
78 self.player.eq(other.player) && self.role.eq(other.role)
79 }
80}
81
82impl Player {
83 #[inline]
84 pub const fn is_black(self) -> bool {
85 matches!(self, Black)
86 }
87
88 #[inline]
89 pub const fn is_white(self) -> bool {
90 matches!(self, White)
91 }
92
93 #[inline]
94 pub const fn other(self) -> Player {
95 match self {
96 Black => White,
97 White => Black,
98 }
99 }
100
101 #[inline]
102 pub const fn backrank(self) -> Rank {
103 match self {
104 Black => Eight,
105 White => One,
106 }
107 }
108
109 #[inline]
110 pub const fn pawn_start_rank(self) -> Rank {
111 match self {
112 Black => Seven,
113 White => Two,
114 }
115 }
116
117 #[inline]
118 pub const fn pawn_push(self) -> Direction {
119 use Direction::*;
120
121 match self {
122 Black => South,
123 White => North,
124 }
125 }
126
127 #[inline]
128 pub const fn pawn_double_push(self) -> Direction {
129 use Direction::*;
130
131 match self {
132 Black => SouthSouth,
133 White => NorthNorth,
134 }
135 }
136
137 #[inline]
138 pub const fn pawn_capture_directions(self) -> [Direction; 2] {
139 use Direction::*;
140
141 match self {
142 Black => [SouthWest, SouthEast],
143 White => [NorthWest, NorthEast],
144 }
145 }
146
147 #[inline]
148 pub const fn pawn_double_push_rank(self) -> Rank {
149 match self {
150 Black => Six,
151 White => Three,
152 }
153 }
154
155 #[inline]
156 pub const fn promotion_rank(self) -> Rank {
157 match self {
158 Black => One,
159 White => Eight,
160 }
161 }
162
163 #[inline]
165 pub const fn pawn(self) -> Piece {
166 Pawn.of(self)
167 }
168
169 #[inline]
170 pub const fn knight(self) -> Piece {
171 Knight.of(self)
172 }
173
174 #[inline]
175 pub const fn bishop(self) -> Piece {
176 Bishop.of(self)
177 }
178
179 #[inline]
180 pub const fn rook(self) -> Piece {
181 Rook.of(self)
182 }
183
184 #[inline]
185 pub const fn queen(self) -> Piece {
186 Queen.of(self)
187 }
188
189 #[inline]
190 pub const fn king(self) -> Piece {
191 King.of(self)
192 }
193
194 #[inline]
196 pub const fn castle_king_to(self, side: Side) -> Square {
197 Square::new(side.king_to_file(), self.backrank())
198 }
199
200 #[inline]
202 pub const fn castle_rook_from(self, file: File) -> Square {
203 Square::new(file, self.backrank())
204 }
205
206 #[inline]
208 pub const fn castle_rook_to(self, side: Side) -> Square {
209 Square::new(side.rook_to_file(), self.backrank())
210 }
211
212 #[inline]
214 pub const fn castle_empty_path(self, king_from: Square, rook_file: File) -> Bitboard {
215 let side = Side::of_rook(king_from, rook_file);
216 let king_to = self.castle_king_to(side);
217 let rook_from = self.castle_rook_from(rook_file);
218 let rook_to = self.castle_rook_to(side);
219
220 let king_path = king_from.between(king_to).with(king_to);
221 let rook_path = rook_from.between(rook_to).with(rook_to);
222
223 king_path
225 .union(rook_path)
226 .difference(Bitboard::from_square(king_from))
227 .difference(Bitboard::from_square(rook_from))
228 }
229
230 #[inline]
232 pub const fn castle_king_path(self, king_from: Square, side: Side) -> Bitboard {
233 let king_to = self.castle_king_to(side);
234 king_from.between(king_to).with(king_from).with(king_to)
235 }
236}
237
238impl FromStr for Player {
239 type Err = &'static str;
240
241 fn from_str(value: &str) -> Result<Self, Self::Err> {
242 let value = value.trim();
243 if value.eq_ignore_ascii_case("black") {
244 Ok(Black)
245 } else if value.eq_ignore_ascii_case("white") {
246 Ok(White)
247 } else {
248 Err("player must be white or black")
249 }
250 }
251}
252
253impl<T> Players<T> {
254 #[inline]
255 pub fn swap(self) -> Players<T> {
256 Players { black: self.white, white: self.black }
257 }
258}
259
260impl Role {
261 #[inline]
262 pub const fn from_char(c: char) -> Option<Self> {
263 Some(match c {
264 'p' | 'P' => Pawn,
265 'n' | 'N' => Knight,
266 'b' | 'B' => Bishop,
267 'r' | 'R' => Rook,
268 'q' | 'Q' => Queen,
269 'k' | 'K' => King,
270 _ => return None,
271 })
272 }
273
274 #[track_caller]
275 #[inline]
276 pub(crate) const fn panicky_from_char(c: char) -> Self {
277 match Self::from_char(c) {
278 Some(role) => role,
279 None => panic!("invalid role"),
280 }
281 }
282
283 #[inline]
285 pub const fn of(self, player: Player) -> Piece {
286 Piece { player, role: self }
287 }
288
289 #[inline]
290 pub const fn lower(self) -> char {
291 match self {
292 Pawn => 'p',
293 Knight => 'n',
294 Bishop => 'b',
295 Rook => 'r',
296 Queen => 'q',
297 King => 'k',
298 }
299 }
300
301 #[inline]
302 pub const fn upper(self) -> char {
303 match self {
304 Pawn => 'P',
305 Knight => 'N',
306 Bishop => 'B',
307 Rook => 'R',
308 Queen => 'Q',
309 King => 'K',
310 }
311 }
312
313 #[inline]
314 pub const fn figurine(self) -> char {
315 match self {
316 Pawn => '♙',
317 Knight => '♘',
318 Bishop => '♗',
319 Rook => '♖',
320 Queen => '♕',
321 King => '♔',
322 }
323 }
324
325 #[inline]
326 pub const fn black(self) -> char {
327 self.lower()
328 }
329
330 #[inline]
331 pub const fn white(self) -> char {
332 self.upper()
333 }
334}