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