1use core::{fmt, ops};
2
3use crate::{
4 Move, Side, Square, finite_for,
5 position::{Error, Result, Special},
6 square::{File, Rank},
7};
8
9mod bitboard;
10mod moves;
11mod piece;
12mod scharnagl;
13
14pub use bitboard::Bitboard;
15pub use piece::*;
16pub use scharnagl::{Scharnagl, scharnagl_by_id};
17
18pub use Player::*;
19pub use Role::*;
20
21#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
28pub struct Board {
38 occupied: Bitboard,
39 players: Players<Bitboard>,
40 roles: Roles<Bitboard>,
41}
42
43#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
45pub struct Parts {
46 pub players: Players<Bitboard>,
47 pub roles: Roles<Bitboard>,
48}
49
50impl fmt::Debug for Board {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 use fmt::Write as _;
53
54 for rank in Rank::ALL.into_iter().rev() {
55 for file in File::ALL {
56 let square = Square::new(file, rank);
57 f.write_char(self.get(square).map_or('.', Piece::char))?;
58 f.write_char(if file < File::H { ' ' } else { '\n' })?;
59 }
60 }
61
62 Ok(())
63 }
64}
65
66impl Board {
68 pub const EMPTY: Board = Board {
69 players: Players { black: Bitboard::EMPTY, white: Bitboard::EMPTY },
70 roles: Roles {
71 pawn: Bitboard::EMPTY,
72 knight: Bitboard::EMPTY,
73 bishop: Bitboard::EMPTY,
74 rook: Bitboard::EMPTY,
75 queen: Bitboard::EMPTY,
76 king: Bitboard::EMPTY,
77 },
78 occupied: Bitboard::EMPTY,
79 };
80
81 #[inline]
82 pub const fn freestyle(i: Scharnagl) -> Board {
83 i.board()
84 }
85
86 #[inline]
87 pub const fn new(parts: Parts) -> Result<Board> {
88 let players = parts.players.black.union(parts.players.white);
89 if !parts.players.black.is_disjoint(parts.players.white) {
90 return Err(Error::InconsistentBoard);
91 }
92
93 let mut roles = Bitboard::EMPTY;
94 finite_for!(role in Role {
95 let squares = parts.roles.get(role);
96 if !roles.is_disjoint(squares) {
97 return Err(Error::InconsistentBoard);
98 }
99 roles.append(squares);
100 });
101
102 if !players.eq(roles) {
103 return Err(Error::InconsistentBoard);
104 }
105
106 Ok(Board { occupied: players, players: parts.players, roles: parts.roles })
107 }
108
109 #[inline]
110 pub const fn parts(self) -> Parts {
111 Parts { players: self.players, roles: self.roles }
112 }
113
114 #[inline]
115 pub const fn standard() -> Board {
116 Board {
117 occupied: Bitboard(0xffff_0000_0000_ffff),
118 players: Players { black: Bitboard(0xffff_0000_0000_0000), white: Bitboard(0xffff) },
119 roles: Roles {
120 pawn: Bitboard(0x00ff_0000_0000_ff00),
121 knight: Bitboard(0x4200_0000_0000_0042),
122 bishop: Bitboard(0x2400_0000_0000_0024),
123 rook: Bitboard(0x8100_0000_0000_0081),
124 queen: Bitboard(0x0800_0000_0000_0008),
125 king: Bitboard(0x1000_0000_0000_0010),
126 },
127 }
128 }
129}
130
131impl Board {
133 #[inline]
134 pub const fn bishops(self) -> Bitboard {
135 self.roles.bishop
136 }
137
138 #[inline]
139 pub const fn bishops_and_queens(self) -> Bitboard {
140 self.bishops().union(self.queens())
141 }
142
143 #[inline]
144 pub const fn black(self) -> Bitboard {
145 self.players.black
146 }
147
148 #[inline]
149 pub const fn king_of(self, player: Player) -> Option<Square> {
150 self.roles.king.intersection(self.player(player)).first()
151 }
152
153 #[inline]
154 pub const fn kings(self) -> Bitboard {
155 self.roles.king
156 }
157
158 #[inline]
159 pub const fn knights(self) -> Bitboard {
160 self.roles.knight
161 }
162
163 #[inline]
164 pub const fn occupied(self) -> Bitboard {
165 self.occupied
166 }
167
168 #[inline]
169 pub const fn pawns(self) -> Bitboard {
170 self.roles.pawn
171 }
172
173 #[inline]
174 pub(super) const fn play_unchecked(&mut self, player: Player, play: Move) {
175 let Some(special) = play.specials() else {
176 self.remove(play.from);
177 self.remove(play.to);
178 self.insert(play.to, play.role.of(player));
179 return;
180 };
181
182 match special {
183 Special::EnPassant => {
184 let captured = Square::new(play.to.file(), play.from.rank());
185 self.remove(play.from);
186 self.remove(captured);
187 self.insert(play.to, player.pawn());
188 }
189 Special::Castle(file) => {
190 let side = Side::of_rook(play.from, file);
191 let rook_from = player.castle_rook_from(file);
192 let rook_to = player.castle_rook_to(side);
193 self.remove(play.from);
194 self.remove(rook_from);
195 self.insert(play.to, player.king());
196 self.insert(rook_to, player.rook());
197 }
198 Special::Promote(role) => {
199 self.remove(play.from);
200 self.remove(play.to);
201 self.insert(play.to, role.of(player));
202 }
203 }
204 }
205
206 #[inline]
207 pub const fn player(self, player: Player) -> Bitboard {
208 self.players.get(player)
209 }
210
211 #[inline]
212 pub const fn player_at(self, square: Square) -> Option<Player> {
213 match self.get(square) {
214 Some(piece) => Some(piece.player),
215 None => None,
216 }
217 }
218
219 #[inline]
220 pub const fn queens(self) -> Bitboard {
221 self.roles.queen
222 }
223
224 #[inline]
225 pub const fn role(self, role: Role) -> Bitboard {
226 self.roles.get(role)
227 }
228
229 #[inline]
230 pub const fn role_at(self, square: Square) -> Option<Role> {
231 match self.get(square) {
232 Some(piece) => Some(piece.role),
233 None => None,
234 }
235 }
236
237 #[inline]
238 pub const fn rooks(self) -> Bitboard {
239 self.roles.rook
240 }
241
242 #[inline]
243 pub const fn rooks_and_queens(self) -> Bitboard {
244 self.rooks().union(self.queens())
245 }
246
247 #[inline]
249 pub const fn sliders(self) -> Bitboard {
250 let Roles { bishop, rook, queen, .. } = self.roles;
251 bishop.symmetric_difference(rook).symmetric_difference(queen)
252 }
253
254 #[inline]
256 pub const fn steppers(self) -> Bitboard {
257 let Roles { pawn, knight, king, .. } = self.roles;
258 pawn.symmetric_difference(knight).symmetric_difference(king)
259 }
260
261 #[inline]
262 pub const fn unique_king_of(self, player: Player) -> Option<Square> {
263 let kings = self.roles.king.intersection(self.player(player));
264 if kings.more_than_one() { None } else { kings.first() }
265 }
266
267 #[inline]
268 pub const fn white(self) -> Bitboard {
269 self.players.white
270 }
271}
272
273impl Board {
275 #[inline]
276 pub const fn clear(&mut self) {
277 *self = Self::EMPTY;
278 }
279
280 #[inline]
281 pub const fn get(self, square: Square) -> Option<Piece> {
282 if !self.occupied.contains(square) {
283 return None;
284 }
285
286 let player = if self.players.black.contains(square) {
287 Black
288 } else if self.players.white.contains(square) {
289 White
290 } else {
291 return None;
292 };
293
294 let role = if self.roles.pawn.contains(square) {
295 Pawn
296 } else if self.roles.knight.contains(square) {
297 Knight
298 } else if self.roles.bishop.contains(square) {
299 Bishop
300 } else if self.roles.rook.contains(square) {
301 Rook
302 } else if self.roles.queen.contains(square) {
303 Queen
304 } else if self.roles.king.contains(square) {
305 King
306 } else {
307 return None;
308 };
309
310 Some(role.of(player))
311 }
312
313 #[inline]
314 pub const fn insert(&mut self, square: Square, piece: Piece) -> Option<Piece> {
315 let previous = self.remove(square);
316 self.occupied.insert(square);
317 self.players.get_mut(piece.player).insert(square);
318 self.roles.get_mut(piece.role).insert(square);
319 previous
320 }
321
322 #[inline]
323 pub const fn remove(&mut self, square: Square) -> Option<Piece> {
324 let Some(piece) = self.get(square) else {
325 return None;
326 };
327 self.occupied.remove(square);
328 self.players.get_mut(piece.player).remove(square);
329 self.roles.get_mut(piece.role).remove(square);
330 Some(piece)
331 }
332}
333
334impl Default for Board {
335 fn default() -> Self {
336 Self::EMPTY
337 }
338}
339
340impl From<Board> for Parts {
341 #[inline]
342 fn from(board: Board) -> Self {
343 board.parts()
344 }
345}
346
347impl TryFrom<Parts> for Board {
348 type Error = Error;
349
350 #[inline]
351 fn try_from(parts: Parts) -> Result<Self> {
352 Board::new(parts)
353 }
354}
355
356impl Parts {
357 #[inline]
358 pub const fn bitor_assign(&mut self, other: Self) {
359 self.players.black.append(other.players.black);
360 self.players.white.append(other.players.white);
361
362 finite_for!(role in Role {
363 self.roles.get_mut(role).append(other.roles.get(role));
364 });
365 }
366}
367
368impl ops::BitOrAssign for Parts {
369 #[inline]
370 fn bitor_assign(&mut self, other: Self) {
371 Parts::bitor_assign(self, other);
372 }
373}