1use crate::bitboard::{BitBoard, EMPTY};
2use crate::board::Board;
3use crate::color::Color;
4use crate::movegen::{MoveList, SquareAndBitBoard};
5use crate::piece::Piece;
6use crate::square::Square;
7
8use crate::magic::{
9 between, get_adjacent_files, get_bishop_moves, get_bishop_rays, get_king_moves,
10 get_knight_moves, get_pawn_attacks, get_pawn_moves, get_rank, get_rook_moves, get_rook_rays,
11 line,
12};
13
14pub trait PieceType {
15 fn is(piece: Piece) -> bool;
16 fn into_piece() -> Piece;
17 fn pseudo_legals(src: Square, color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard;
18 #[inline(always)]
19 fn legals<T>(
20 movelist: &mut MoveList,
21 board: &Board,
22 mask: BitBoard,
23 from_square: Option<Square>,
24 ) where
25 T: CheckType,
26 {
27 let combined = board.combined();
28 let color = board.side_to_move();
29 let my_pieces = board.color_combined(color);
30 let ksq = board.king_square(color);
31
32 let from_mask = match from_square {
33 Some(from) => BitBoard::from_square(from),
34 None => *my_pieces,
35 };
36
37 let pieces = board.pieces(Self::into_piece()) & from_mask;
38 let pinned = board.pinned();
39 let checkers = board.checkers();
40
41 let check_mask = if T::IN_CHECK {
42 between(checkers.to_square(), ksq) ^ checkers
43 } else {
44 !EMPTY
45 };
46
47 for src in pieces & !pinned {
48 let moves = Self::pseudo_legals(src, color, *combined, mask) & check_mask;
49 if moves != EMPTY {
50 unsafe {
51 movelist.push_unchecked(SquareAndBitBoard::new(src, moves, false));
52 }
53 }
54 }
55
56 if !T::IN_CHECK {
57 for src in pieces & pinned {
58 let moves = Self::pseudo_legals(src, color, *combined, mask) & line(src, ksq);
59 if moves != EMPTY {
60 unsafe {
61 movelist.push_unchecked(SquareAndBitBoard::new(src, moves, false));
62 }
63 }
64 }
65 }
66 }
67}
68
69pub struct PawnType;
70pub struct BishopType;
71pub struct KnightType;
72pub struct RookType;
73pub struct QueenType;
74pub struct KingType;
75
76pub trait CheckType {
77 const IN_CHECK: bool;
78}
79
80pub struct InCheckType;
81pub struct NotInCheckType;
82
83impl CheckType for InCheckType {
84 const IN_CHECK: bool = true;
85}
86
87impl CheckType for NotInCheckType {
88 const IN_CHECK: bool = false;
89}
90
91impl PawnType {
92 pub fn legal_ep_move(board: &Board, source: Square, dest: Square) -> bool {
94 let combined = board.combined()
95 ^ BitBoard::from_square(board.en_passant().unwrap())
96 ^ BitBoard::from_square(source)
97 ^ BitBoard::from_square(dest);
98
99 let ksq =
100 (board.pieces(Piece::King) & board.color_combined(board.side_to_move())).to_square();
101
102 let rooks = (board.pieces(Piece::Rook) | board.pieces(Piece::Queen))
103 & board.color_combined(!board.side_to_move());
104
105 if (get_rook_rays(ksq) & rooks) != EMPTY {
106 if (get_rook_moves(ksq, combined) & rooks) != EMPTY {
107 return false;
108 }
109 }
110
111 let bishops = (board.pieces(Piece::Bishop) | board.pieces(Piece::Queen))
112 & board.color_combined(!board.side_to_move());
113
114 if (get_bishop_rays(ksq) & bishops) != EMPTY {
115 if (get_bishop_moves(ksq, combined) & bishops) != EMPTY {
116 return false;
117 }
118 }
119
120 return true;
121 }
122}
123
124impl PieceType for PawnType {
125 fn is(piece: Piece) -> bool {
126 piece == Piece::Pawn
127 }
128
129 fn into_piece() -> Piece {
130 Piece::Pawn
131 }
132
133 #[inline(always)]
134 fn pseudo_legals(src: Square, color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard {
135 get_pawn_moves(src, color, combined) & mask
136 }
137
138 #[inline(always)]
139 fn legals<T>(
140 movelist: &mut MoveList,
141 board: &Board,
142 mask: BitBoard,
143 from_square: Option<Square>,
144 ) where
145 T: CheckType,
146 {
147 let combined = board.combined();
148 let color = board.side_to_move();
149 let my_pieces = board.color_combined(color);
150 let ksq = board.king_square(color);
151
152 let from_mask = match from_square {
153 Some(from) => BitBoard::from_square(from),
154 None => *my_pieces,
155 };
156
157 let pieces = board.pieces(Self::into_piece()) & from_mask;
158 let pinned = board.pinned();
159 let checkers = board.checkers();
160
161 let check_mask = if T::IN_CHECK {
162 between(checkers.to_square(), ksq) ^ checkers
163 } else {
164 !EMPTY
165 };
166
167 for src in pieces & !pinned {
168 let moves = Self::pseudo_legals(src, color, *combined, mask) & check_mask;
169 if moves != EMPTY {
170 unsafe {
171 movelist.push_unchecked(SquareAndBitBoard::new(
172 src,
173 moves,
174 src.get_rank() == color.to_seventh_rank(),
175 ));
176 }
177 }
178 }
179
180 if !T::IN_CHECK {
181 for src in pieces & pinned {
182 let moves = Self::pseudo_legals(src, color, *combined, mask) & line(ksq, src);
183 if moves != EMPTY {
184 unsafe {
185 movelist.push_unchecked(SquareAndBitBoard::new(
186 src,
187 moves,
188 src.get_rank() == color.to_seventh_rank(),
189 ));
190 }
191 }
192 }
193 }
194
195 if board.en_passant().is_some() {
196 let ep_sq = board.en_passant().unwrap();
197 let rank = get_rank(ep_sq.get_rank());
198 let files = get_adjacent_files(ep_sq.get_file());
199 for src in rank & files & pieces {
200 let dest = ep_sq.uforward(color);
201 if PawnType::legal_ep_move(board, src, dest) {
202 unsafe {
203 movelist.push_unchecked(SquareAndBitBoard::new(
204 src,
205 BitBoard::from_square(dest),
206 false,
207 ));
208 }
209 }
210 }
211 }
212 }
213}
214
215impl PieceType for BishopType {
216 fn is(piece: Piece) -> bool {
217 piece == Piece::Bishop
218 }
219
220 fn into_piece() -> Piece {
221 Piece::Bishop
222 }
223
224 #[inline(always)]
225 fn pseudo_legals(src: Square, _color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard {
226 get_bishop_moves(src, combined) & mask
227 }
228}
229
230impl PieceType for KnightType {
231 fn is(piece: Piece) -> bool {
232 piece == Piece::Knight
233 }
234
235 fn into_piece() -> Piece {
236 Piece::Knight
237 }
238
239 #[inline(always)]
240 fn pseudo_legals(src: Square, _color: Color, _combined: BitBoard, mask: BitBoard) -> BitBoard {
241 get_knight_moves(src) & mask
242 }
243
244 #[inline(always)]
245 fn legals<T>(
246 movelist: &mut MoveList,
247 board: &Board,
248 mask: BitBoard,
249 from_square: Option<Square>,
250 ) where
251 T: CheckType,
252 {
253 let combined = board.combined();
254 let color = board.side_to_move();
255 let my_pieces = board.color_combined(color);
256 let ksq = board.king_square(color);
257
258 let from_mask = match from_square {
259 Some(from) => BitBoard::from_square(from),
260 None => *my_pieces,
261 };
262
263 let pieces = board.pieces(Self::into_piece()) & from_mask;
264 let pinned = board.pinned();
265 let checkers = board.checkers();
266
267 if T::IN_CHECK {
268 let check_mask = between(checkers.to_square(), ksq) ^ checkers;
269
270 for src in pieces & !pinned {
271 let moves = Self::pseudo_legals(src, color, *combined, mask & check_mask);
272 if moves != EMPTY {
273 unsafe {
274 movelist.push_unchecked(SquareAndBitBoard::new(src, moves, false));
275 }
276 }
277 }
278 } else {
279 for src in pieces & !pinned {
280 let moves = Self::pseudo_legals(src, color, *combined, mask);
281 if moves != EMPTY {
282 unsafe {
283 movelist.push_unchecked(SquareAndBitBoard::new(src, moves, false));
284 }
285 }
286 }
287 };
288 }
289}
290
291impl PieceType for RookType {
292 fn is(piece: Piece) -> bool {
293 piece == Piece::Rook
294 }
295
296 fn into_piece() -> Piece {
297 Piece::Rook
298 }
299
300 #[inline(always)]
301 fn pseudo_legals(src: Square, _color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard {
302 get_rook_moves(src, combined) & mask
303 }
304}
305
306impl PieceType for QueenType {
307 fn is(piece: Piece) -> bool {
308 piece == Piece::Queen
309 }
310
311 fn into_piece() -> Piece {
312 Piece::Queen
313 }
314
315 #[inline(always)]
316 fn pseudo_legals(src: Square, _color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard {
317 (get_rook_moves(src, combined) ^ get_bishop_moves(src, combined)) & mask
318 }
319}
320
321impl KingType {
322 #[inline(always)]
324 pub fn legal_king_move(board: &Board, dest: Square) -> bool {
325 let combined = board.combined()
326 ^ (board.pieces(Piece::King) & board.color_combined(board.side_to_move()))
327 | BitBoard::from_square(dest);
328
329 let mut attackers = EMPTY;
330
331 let rooks = (board.pieces(Piece::Rook) | board.pieces(Piece::Queen))
332 & board.color_combined(!board.side_to_move());
333
334 attackers |= get_rook_moves(dest, combined) & rooks;
335
336 let bishops = (board.pieces(Piece::Bishop) | board.pieces(Piece::Queen))
337 & board.color_combined(!board.side_to_move());
338
339 attackers |= get_bishop_moves(dest, combined) & bishops;
340
341 let knight_rays = get_knight_moves(dest);
342 attackers |=
343 knight_rays & board.pieces(Piece::Knight) & board.color_combined(!board.side_to_move());
344
345 let king_rays = get_king_moves(dest);
346 attackers |=
347 king_rays & board.pieces(Piece::King) & board.color_combined(!board.side_to_move());
348
349 attackers |= get_pawn_attacks(
350 dest,
351 board.side_to_move(),
352 board.pieces(Piece::Pawn) & board.color_combined(!board.side_to_move()),
353 );
354
355 return attackers == EMPTY;
356 }
357}
358
359impl PieceType for KingType {
360 fn is(piece: Piece) -> bool {
361 piece == Piece::King
362 }
363
364 fn into_piece() -> Piece {
365 Piece::King
366 }
367
368 #[inline(always)]
369 fn pseudo_legals(src: Square, _color: Color, _combined: BitBoard, mask: BitBoard) -> BitBoard {
370 get_king_moves(src) & mask
371 }
372
373 #[inline(always)]
374 fn legals<T>(
375 movelist: &mut MoveList,
376 board: &Board,
377 mask: BitBoard,
378 from_square: Option<Square>, ) where
380 T: CheckType,
381 {
382 let combined = board.combined();
383 let color = board.side_to_move();
384 let ksq = board.king_square(color);
385
386 let mut moves = Self::pseudo_legals(ksq, color, *combined, mask);
387
388 let copy = moves;
389 for dest in copy {
390 if !KingType::legal_king_move(board, dest) {
391 moves ^= BitBoard::from_square(dest);
392 }
393 }
394
395 if !T::IN_CHECK {
404 if board.my_castle_rights().has_kingside()
405 && (combined & board.my_castle_rights().kingside_squares(color)) == EMPTY
406 {
407 let middle = ksq.uright();
408 let right = middle.uright();
409 if KingType::legal_king_move(board, middle)
410 && KingType::legal_king_move(board, right)
411 {
412 moves ^= BitBoard::from_square(right);
413 }
414 }
415
416 if board.my_castle_rights().has_queenside()
417 && (combined & board.my_castle_rights().queenside_squares(color)) == EMPTY
418 {
419 let middle = ksq.uleft();
420 let left = middle.uleft();
421 if KingType::legal_king_move(board, middle)
422 && KingType::legal_king_move(board, left)
423 {
424 moves ^= BitBoard::from_square(left);
425 }
426 }
427 }
428 if moves != EMPTY {
429 unsafe {
430 movelist.push_unchecked(SquareAndBitBoard::new(ksq, moves, false));
431 }
432 }
433 }
434}