1use super::*;
2
3trait PieceMoves {
4 fn is(piece: PieceType) -> bool;
5 fn into_piece() -> PieceType;
6 fn pseudo_legals(src: Square, color: Color, occupied: BitBoard, mask: BitBoard) -> BitBoard;
7 fn legals<T>(move_list: &mut MoveList, position: &ChessPosition, mask: BitBoard)
8 where
9 T: CheckMoves,
10 {
11 let occupied = position.occupied();
12 let color = position.turn();
13 let ksq = position.get_king_square(color);
14
15 let pieces = position.get_colored_piece_mask(Self::into_piece(), color);
16 let pinned = position.pinned();
17 let checkers = position.get_checkers();
18
19 let check_mask = if T::IN_CHECK {
20 unsafe { checkers.to_square_unchecked() }.between(ksq) ^ checkers
21 } else {
22 BitBoard::ALL
23 };
24
25 for src in pieces & !pinned {
26 let square_and_bitboard_array =
27 Self::pseudo_legals(src, color, occupied, mask) & check_mask;
28 if !square_and_bitboard_array.is_empty() {
29 unsafe {
30 move_list.push_unchecked(SquareAndBitBoard::new(
31 src,
32 square_and_bitboard_array,
33 false,
34 ));
35 }
36 }
37 }
38
39 if !T::IN_CHECK {
40 for src in pieces & pinned {
41 let square_and_bitboard_array =
42 Self::pseudo_legals(src, color, occupied, mask) & src.line(ksq);
43 if !square_and_bitboard_array.is_empty() {
44 unsafe {
45 move_list.push_unchecked(SquareAndBitBoard::new(
46 src,
47 square_and_bitboard_array,
48 false,
49 ));
50 }
51 }
52 }
53 }
54 }
55}
56
57struct PawnMoves;
58struct BishopMoves;
59struct KnightMoves;
60struct RookMoves;
61struct QueenMoves;
62struct KingMoves;
63
64trait CheckMoves {
65 const IN_CHECK: bool;
66}
67
68struct InCheckMoves;
69struct NotInCheckMoves;
70
71impl CheckMoves for InCheckMoves {
72 const IN_CHECK: bool = true;
73}
74
75impl CheckMoves for NotInCheckMoves {
76 const IN_CHECK: bool = false;
77}
78
79impl PawnMoves {
80 fn legal_ep_move(position: &ChessPosition, source: Square, dest: Square) -> bool {
81 let Some(ep_square) = position.ep_square() else {
82 return false;
83 };
84
85 let occupied = position.occupied()
86 ^ ep_square.wrapping_backward(position.turn()).to_bitboard()
87 ^ source.to_bitboard()
88 ^ dest.to_bitboard();
89
90 let ksq = unsafe {
91 (position.get_colored_piece_mask(King, position.turn())).to_square_unchecked()
92 };
93
94 let rooks = (position.get_piece_mask(Rook) ^ position.get_piece_mask(Queen))
95 & position.opponent_occupied();
96
97 if !(ksq.get_rook_rays_bb() & rooks).is_empty()
98 && !(ksq.get_rook_moves(occupied) & rooks).is_empty()
99 {
100 return false;
101 }
102
103 let bishops = (position.get_piece_mask(Bishop) ^ position.get_piece_mask(Queen))
104 & position.opponent_occupied();
105
106 if !(ksq.get_bishop_rays_bb() & bishops).is_empty()
107 && !(ksq.get_bishop_moves(occupied) & bishops).is_empty()
108 {
109 return false;
110 }
111
112 true
113 }
114}
115
116impl PieceMoves for PawnMoves {
117 fn is(piece: PieceType) -> bool {
118 piece == Pawn
119 }
120
121 fn into_piece() -> PieceType {
122 Pawn
123 }
124
125 #[inline]
126 fn pseudo_legals(src: Square, color: Color, occupied: BitBoard, mask: BitBoard) -> BitBoard {
127 src.get_pawn_moves(color, occupied) & mask
128 }
129
130 #[inline]
131 fn legals<T>(move_list: &mut MoveList, position: &ChessPosition, mask: BitBoard)
132 where
133 T: CheckMoves,
134 {
135 let occupied = position.occupied();
136 let color = position.turn();
137 let ksq = position.get_king_square(color);
138
139 let pieces = position.get_colored_piece_mask(Self::into_piece(), color);
140 let pinned = position.pinned();
141 let checkers = position.get_checkers();
142
143 let check_mask = if T::IN_CHECK {
144 unsafe { checkers.to_square_unchecked() }.between(ksq) ^ checkers
145 } else {
146 BitBoard::ALL
147 };
148
149 for src in pieces & !pinned {
150 let square_and_bitboard_array =
151 Self::pseudo_legals(src, color, occupied, mask) & check_mask;
152 if !square_and_bitboard_array.is_empty() {
153 unsafe {
154 move_list.push_unchecked(SquareAndBitBoard::new(
155 src,
156 square_and_bitboard_array,
157 src.get_rank() == color.to_seventh_rank(),
158 ));
159 }
160 }
161 }
162
163 if !T::IN_CHECK {
164 for src in pieces & pinned {
165 let square_and_bitboard_array =
166 Self::pseudo_legals(src, color, occupied, mask) & ksq.line(src);
167 if !square_and_bitboard_array.is_empty() {
168 unsafe {
169 move_list.push_unchecked(SquareAndBitBoard::new(
170 src,
171 square_and_bitboard_array,
172 src.get_rank() == color.to_seventh_rank(),
173 ));
174 }
175 }
176 }
177 }
178
179 if let Some(dest) = position.ep_square() {
180 let dest_rank = dest.get_rank();
181 let rank_bb = if dest_rank.to_int() > 3 {
182 dest_rank.wrapping_down().to_bitboard()
183 } else {
184 dest_rank.wrapping_up().to_bitboard()
185 };
186 let files_bb = dest.get_file().get_adjacent_files_bb();
187 for src in rank_bb & files_bb & pieces {
188 if Self::legal_ep_move(position, src, dest) {
189 unsafe {
190 move_list.push_unchecked(SquareAndBitBoard::new(
191 src,
192 dest.to_bitboard(),
193 false,
194 ));
195 }
196 }
197 }
198 }
199 }
200}
201
202impl PieceMoves for BishopMoves {
203 fn is(piece: PieceType) -> bool {
204 piece == Bishop
205 }
206
207 fn into_piece() -> PieceType {
208 Bishop
209 }
210
211 #[inline]
212 fn pseudo_legals(src: Square, _: Color, occupied: BitBoard, mask: BitBoard) -> BitBoard {
213 src.get_bishop_moves(occupied) & mask
214 }
215}
216
217impl PieceMoves for KnightMoves {
218 fn is(piece: PieceType) -> bool {
219 piece == Knight
220 }
221
222 fn into_piece() -> PieceType {
223 Knight
224 }
225
226 #[inline]
227 fn pseudo_legals(src: Square, _: Color, _: BitBoard, mask: BitBoard) -> BitBoard {
228 src.get_knight_moves() & mask
229 }
230
231 #[inline]
232 fn legals<T>(move_list: &mut MoveList, position: &ChessPosition, mask: BitBoard)
233 where
234 T: CheckMoves,
235 {
236 let occupied = position.occupied();
237 let color = position.turn();
238 let ksq = position.get_king_square(color);
239
240 let pieces = position.get_colored_piece_mask(Self::into_piece(), color);
241 let pinned = position.pinned();
242 let checkers = position.get_checkers();
243
244 if T::IN_CHECK {
245 let check_mask = unsafe { checkers.to_square_unchecked() }.between(ksq) ^ checkers;
246
247 for src in pieces & !pinned {
248 let square_and_bitboard_array =
249 Self::pseudo_legals(src, color, occupied, mask & check_mask);
250 if !square_and_bitboard_array.is_empty() {
251 unsafe {
252 move_list.push_unchecked(SquareAndBitBoard::new(
253 src,
254 square_and_bitboard_array,
255 false,
256 ));
257 }
258 }
259 }
260 } else {
261 for src in pieces & !pinned {
262 let square_and_bitboard_array = Self::pseudo_legals(src, color, occupied, mask);
263 if !square_and_bitboard_array.is_empty() {
264 unsafe {
265 move_list.push_unchecked(SquareAndBitBoard::new(
266 src,
267 square_and_bitboard_array,
268 false,
269 ));
270 }
271 }
272 }
273 };
274 }
275}
276
277impl PieceMoves for RookMoves {
278 fn is(piece: PieceType) -> bool {
279 piece == Rook
280 }
281
282 fn into_piece() -> PieceType {
283 Rook
284 }
285
286 #[inline]
287 fn pseudo_legals(src: Square, _: Color, occupied: BitBoard, mask: BitBoard) -> BitBoard {
288 src.get_rook_moves(occupied) & mask
289 }
290}
291
292impl PieceMoves for QueenMoves {
293 fn is(piece: PieceType) -> bool {
294 piece == Queen
295 }
296
297 fn into_piece() -> PieceType {
298 Queen
299 }
300
301 #[inline]
302 fn pseudo_legals(src: Square, _: Color, occupied: BitBoard, mask: BitBoard) -> BitBoard {
303 src.get_queen_moves(occupied) & mask
304 }
305}
306
307impl KingMoves {
308 #[inline]
309 fn legal_king_move(position: &ChessPosition, dest: Square) -> bool {
310 let occupied = position.occupied() ^ position.get_colored_piece_mask(King, position.turn())
311 | dest.to_bitboard();
312
313 let rooks = (position.get_piece_mask(Rook) ^ position.get_piece_mask(Queen))
314 & position.opponent_occupied();
315
316 let mut attackers = dest.get_rook_moves(occupied) & rooks;
317
318 let bishops = (position.get_piece_mask(Bishop) ^ position.get_piece_mask(Queen))
319 & position.opponent_occupied();
320
321 attackers |= dest.get_bishop_moves(occupied) & bishops;
322
323 let knight_rays = dest.get_knight_moves();
324
325 attackers ^= knight_rays & position.get_colored_piece_mask(Knight, !position.turn());
327 attackers |=
328 dest.get_king_moves() & position.get_colored_piece_mask(King, !position.turn());
329 attackers |= dest.get_pawn_attacks(
330 position.turn(),
331 position.get_colored_piece_mask(Pawn, !position.turn()),
332 );
333
334 attackers.is_empty()
335 }
336}
337
338impl PieceMoves for KingMoves {
339 fn is(piece: PieceType) -> bool {
340 piece == King
341 }
342
343 fn into_piece() -> PieceType {
344 King
345 }
346
347 #[inline]
348 fn pseudo_legals(src: Square, _: Color, _: BitBoard, mask: BitBoard) -> BitBoard {
349 src.get_king_moves() & mask
350 }
351
352 #[inline]
353 fn legals<T>(move_list: &mut MoveList, position: &ChessPosition, mask: BitBoard)
354 where
355 T: CheckMoves,
356 {
357 let occupied = position.occupied();
358 let color = position.turn();
359 let ksq = position.get_king_square(color);
360
361 let mut square_and_bitboard_array = Self::pseudo_legals(ksq, color, occupied, mask);
362
363 let copy = square_and_bitboard_array;
364 for dest in copy {
365 if !Self::legal_king_move(position, dest) {
366 square_and_bitboard_array ^= dest.to_bitboard();
367 }
368 }
369
370 if !T::IN_CHECK {
379 if position.my_castle_rights().has_kingside()
380 && (occupied & position.my_castle_rights().kingside_squares(color)).is_empty()
381 {
382 let middle = ksq.wrapping_right();
383 let right = middle.wrapping_right();
384 if Self::legal_king_move(position, middle) && Self::legal_king_move(position, right)
385 {
386 square_and_bitboard_array ^= right.to_bitboard();
387 }
388 }
389
390 if position.my_castle_rights().has_queenside()
391 && (occupied & position.my_castle_rights().queenside_squares(color)).is_empty()
392 {
393 let middle = ksq.wrapping_left();
394 let left = middle.wrapping_left();
395 if Self::legal_king_move(position, middle) && Self::legal_king_move(position, left)
396 {
397 square_and_bitboard_array ^= left.to_bitboard();
398 }
399 }
400 }
401 if !square_and_bitboard_array.is_empty() {
402 unsafe {
403 move_list.push_unchecked(SquareAndBitBoard::new(
404 ksq,
405 square_and_bitboard_array,
406 false,
407 ));
408 }
409 }
410 }
411}
412
413#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
414#[derive(Clone, PartialEq, Eq, Debug)]
415struct SquareAndBitBoard {
416 square: Square,
417 bitboard: BitBoard,
418 promotion: bool,
419}
420
421impl SquareAndBitBoard {
422 fn new(square: Square, bb: BitBoard, promotion: bool) -> Self {
423 Self {
424 square,
425 bitboard: bb,
426 promotion,
427 }
428 }
429}
430
431type MoveList = ArrayVec<SquareAndBitBoard, 18>;
432
433#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
434#[derive(Clone, Debug)]
435pub struct MoveGenerator {
436 square_and_bitboard_array: MoveList,
437 promotion_index: usize,
438 from_bitboard_iterator_mask: BitBoard,
439 to_bitboard_iterator_mask: BitBoard,
440}
441
442impl MoveGenerator {
443 #[inline]
444 fn enumerate_moves(position: &ChessPosition) -> MoveList {
445 let checkers = position.get_checkers();
446 let mask = !position.self_occupied();
447 let mut move_list = ArrayVec::new();
448
449 if checkers.is_empty() {
450 PawnMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
451 KnightMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
452 BishopMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
453 RookMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
454 QueenMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
455 KingMoves::legals::<NotInCheckMoves>(&mut move_list, position, mask);
456 } else if checkers.popcnt() == 1 {
457 PawnMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
458 KnightMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
459 BishopMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
460 RookMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
461 QueenMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
462 KingMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
463 } else {
464 KingMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
465 }
466
467 move_list
468 }
469
470 #[inline]
471 pub fn get_single_legal_move(
472 position: &ChessPosition,
473 from_bitboard: BitBoard,
474 to_bitboard: BitBoard,
475 ) -> Option<Move> {
476 let checkers = position.get_checkers();
477 let mask = !position.self_occupied() & to_bitboard;
478 let mut move_list = ArrayVec::new();
479
480 let legal_functions = if checkers.is_empty() {
481 [
482 PawnMoves::legals::<NotInCheckMoves>,
483 KnightMoves::legals::<NotInCheckMoves>,
484 BishopMoves::legals::<NotInCheckMoves>,
485 RookMoves::legals::<NotInCheckMoves>,
486 QueenMoves::legals::<NotInCheckMoves>,
487 KingMoves::legals::<NotInCheckMoves>,
488 ]
489 } else if checkers.popcnt() == 1 {
490 [
491 PawnMoves::legals::<InCheckMoves>,
492 KnightMoves::legals::<InCheckMoves>,
493 BishopMoves::legals::<InCheckMoves>,
494 RookMoves::legals::<InCheckMoves>,
495 QueenMoves::legals::<InCheckMoves>,
496 KingMoves::legals::<InCheckMoves>,
497 ]
498 } else {
499 KingMoves::legals::<InCheckMoves>(&mut move_list, position, mask);
500 return move_list.into_iter().find_map(|square_and_bitboard| {
501 (from_bitboard.contains(square_and_bitboard.square)
502 && !(square_and_bitboard.bitboard & to_bitboard).is_empty())
503 .then(|| unsafe {
504 Move::new_unchecked(
505 square_and_bitboard.square,
506 square_and_bitboard.bitboard.to_square_unchecked(),
507 square_and_bitboard.promotion.then_some(Queen),
508 )
509 })
510 });
511 };
512
513 let mut start_index = 0;
514
515 for function in legal_functions {
516 function(&mut move_list, position, mask);
517 for square_and_bitboard in &move_list[start_index..] {
518 if from_bitboard.contains(square_and_bitboard.square)
519 && !(square_and_bitboard.bitboard & to_bitboard).is_empty()
520 {
521 return Some(unsafe {
522 Move::new_unchecked(
523 square_and_bitboard.square,
524 square_and_bitboard.bitboard.to_square_unchecked(),
525 square_and_bitboard.promotion.then_some(Queen),
526 )
527 });
528 }
529 }
530 start_index = move_list.len();
531 }
532
533 None
534 }
535
536 #[inline]
537 pub fn has_legal_moves(
538 position: &ChessPosition,
539 from_bitboard: BitBoard,
540 to_bitboard: BitBoard,
541 ) -> bool {
542 Self::get_single_legal_move(position, from_bitboard, to_bitboard).is_some()
543 }
544
545 #[inline]
546 pub fn new_legal(position: &ChessPosition) -> Self {
547 Self {
548 square_and_bitboard_array: Self::enumerate_moves(position),
549 promotion_index: 0,
550 from_bitboard_iterator_mask: BitBoard::ALL,
551 to_bitboard_iterator_mask: BitBoard::ALL,
552 }
553 }
554
555 pub fn reorganize_square_and_bitboard_array(&mut self) {
560 let mut i = 0;
562 while i < self.square_and_bitboard_array.len()
563 && !(get_item_unchecked!(self.square_and_bitboard_array, i).bitboard
564 & self.to_bitboard_iterator_mask)
565 .is_empty()
566 {
567 i += 1;
568 }
569
570 for j in (i + 1)..self.square_and_bitboard_array.len() {
573 if !(get_item_unchecked!(self.square_and_bitboard_array, j).bitboard
574 & self.to_bitboard_iterator_mask)
575 .is_empty()
576 {
577 self.square_and_bitboard_array.swap(i, j);
579 i += 1;
580 }
581 }
582 }
583
584 pub fn remove_mask(&mut self, mask: BitBoard) {
585 self.square_and_bitboard_array
586 .iter_mut()
587 .for_each(|square_and_bitboard| square_and_bitboard.bitboard &= !mask);
588 self.reorganize_square_and_bitboard_array();
589 }
590
591 pub fn remove_move(&mut self, move_: Move) -> bool {
592 let mut square_removed = false;
593 for square_and_bitboard in self.square_and_bitboard_array.iter_mut() {
594 if square_and_bitboard.square == move_.get_source() {
595 square_and_bitboard.bitboard &= !move_.get_dest().to_bitboard();
596 square_removed = true;
597 }
598 }
599 self.reorganize_square_and_bitboard_array();
600 square_removed
601 }
602
603 pub fn get_from_bitboard_iterator_mask(&self) -> BitBoard {
604 self.from_bitboard_iterator_mask
605 }
606
607 pub fn set_from_bitboard_iterator_mask(&mut self, mask: BitBoard) {
608 self.from_bitboard_iterator_mask = mask;
609 self.reorganize_square_and_bitboard_array();
610 }
611
612 pub fn get_to_bitboard_iterator_mask(&self) -> BitBoard {
613 self.to_bitboard_iterator_mask
614 }
615
616 pub fn set_to_bitboard_iterator_mask(&mut self, mask: BitBoard) {
617 self.to_bitboard_iterator_mask = mask;
618 self.reorganize_square_and_bitboard_array();
619 }
620
621 pub fn set_iterator_masks(&mut self, from_bitboard: BitBoard, to_bitboard: BitBoard) {
622 self.from_bitboard_iterator_mask = from_bitboard;
623 self.to_bitboard_iterator_mask = to_bitboard;
624 self.reorganize_square_and_bitboard_array();
625 }
626
627 pub fn perft_test(position: &ChessPosition, depth: usize) -> usize {
628 let iterable = position.generate_legal_moves();
629
630 let mut result: usize = 0;
631 if depth == 1 {
632 iterable.len()
633 } else {
634 for m in iterable {
635 let board_result = position.make_move_new(m);
636 result += Self::perft_test(&board_result, depth - 1);
637 }
638 result
639 }
640 }
641
642 pub fn perft_test_piecewise(position: &ChessPosition, depth: usize) -> usize {
643 let mut iterable = position.generate_legal_moves();
644
645 let targets = position.opponent_occupied();
646 let mut result: usize = 0;
647
648 for &piece_mask in position.get_all_piece_masks() {
649 iterable.set_from_bitboard_iterator_mask(piece_mask);
650 iterable.set_to_bitboard_iterator_mask(targets);
651 if depth == 1 {
652 result += iterable.len();
653 iterable.set_to_bitboard_iterator_mask(!targets);
654 result += iterable.len();
655 } else {
656 for x in &iterable {
657 result += Self::perft_test(&position.make_move_new(x), depth - 1);
658 }
659 iterable.set_to_bitboard_iterator_mask(!targets);
660 for x in &iterable {
661 result += Self::perft_test(&position.make_move_new(x), depth - 1);
662 }
663 }
664 }
665 result
666 }
667
668 #[inline]
669 pub fn iter(&self) -> impl Iterator<Item = Move> {
670 let iterator = self
671 .square_and_bitboard_array
672 .iter()
673 .take_while(|square_and_bitboard| {
674 !(square_and_bitboard.bitboard & self.to_bitboard_iterator_mask).is_empty()
675 })
676 .filter(|square_and_bitboard| {
677 self.from_bitboard_iterator_mask
678 .contains(square_and_bitboard.square)
679 })
680 .flat_map(move |square_and_bitboard| {
681 let promotion_pieces: &[Option<PieceType>] = if square_and_bitboard.promotion {
682 const { &[Some(Queen), Some(Knight), Some(Rook), Some(Bishop)] }
683 } else {
684 const { &[None] }
685 };
686 promotion_pieces.iter().flat_map(move |&promotion| {
687 (square_and_bitboard.bitboard & self.to_bitboard_iterator_mask).map(
688 move |dest| unsafe {
689 Move::new_unchecked(square_and_bitboard.square, dest, promotion)
690 },
691 )
692 })
693 });
694
695 MoveGeneratorReferencedIterator {
696 move_generator: self,
697 iterator,
698 }
699 }
700
701 pub fn len(&self) -> usize {
702 let mut result = 0;
703 for square_and_bitboard in &self.square_and_bitboard_array {
704 let bitboard_and_to_bitboard_iterator_mask =
705 square_and_bitboard.bitboard & self.to_bitboard_iterator_mask;
706 if !self
707 .from_bitboard_iterator_mask
708 .contains(square_and_bitboard.square)
709 || bitboard_and_to_bitboard_iterator_mask.is_empty()
710 {
711 break;
712 }
713 if square_and_bitboard.promotion {
714 result += (bitboard_and_to_bitboard_iterator_mask.popcnt() as usize)
715 * NUM_PROMOTION_PIECES;
716 } else {
717 result += bitboard_and_to_bitboard_iterator_mask.popcnt() as usize;
718 }
719 }
720 result
721 }
722
723 #[inline]
724 pub fn is_empty(&self) -> bool {
725 self.len() == 0
726 }
727
728 #[inline]
729 pub fn contains(&self, move_: &Move) -> bool {
730 self.square_and_bitboard_array
731 .iter()
732 .any(|square_and_bitboard| {
733 square_and_bitboard.square == move_.get_source()
734 && square_and_bitboard.bitboard.contains(move_.get_dest())
735 && if square_and_bitboard.promotion {
736 const { [Some(Knight), Some(Bishop), Some(Rook), Some(Queen)] }
738 .contains(&move_.get_promotion())
739 } else {
740 move_.get_promotion().is_none()
741 }
742 })
743 }
744
745 #[inline]
746 pub fn is_legal(position: &ChessPosition, move_: &Move) -> bool {
747 let Some(piece_type) = position.get_piece_type_at(move_.get_source()) else {
749 return false;
750 };
751 let possibly_legal = match piece_type {
752 Pawn => {
753 if move_.get_source().get_file() != move_.get_dest().get_file()
754 && position.get_piece_type_at(move_.get_dest()).is_none()
755 {
756 PawnMoves::legal_ep_move(position, move_.get_source(), move_.get_dest())
758 } else {
759 true
760 }
761 }
762 King => {
763 let bb = move_.get_source().between(move_.get_dest());
764 if bb.popcnt() == 1 {
765 if !KingMoves::legal_king_move(position, unsafe { bb.to_square_unchecked() }) {
767 false
768 } else {
769 KingMoves::legal_king_move(position, move_.get_dest())
770 }
771 } else {
772 KingMoves::legal_king_move(position, move_.get_dest())
773 }
774 }
775 _ => true,
776 };
777 if !possibly_legal {
778 return false;
779 }
780 position.generate_legal_moves().contains(move_)
781 }
782}
783
784pub struct MoveGeneratorReferencedIterator<'a, T: Iterator<Item = Move>> {
785 move_generator: &'a MoveGenerator,
786 iterator: T,
787}
788
789impl<T: Iterator<Item = Move>> ExactSizeIterator for MoveGeneratorReferencedIterator<'_, T> {
790 #[inline]
791 fn len(&self) -> usize {
792 self.move_generator.len()
793 }
794}
795
796impl<T: Iterator<Item = Move>> Iterator for MoveGeneratorReferencedIterator<'_, T> {
797 type Item = Move;
798
799 fn size_hint(&self) -> (usize, Option<usize>) {
800 let len = self.len();
801 (len, Some(len))
802 }
803
804 fn next(&mut self) -> Option<Move> {
805 self.iterator.next()
806 }
807}
808
809impl<'a> IntoIterator for &'a MoveGenerator {
810 type Item = Move;
811 type IntoIter = Box<dyn Iterator<Item = Move> + 'a>;
813
814 fn into_iter(self) -> Self::IntoIter {
815 Box::new(self.iter())
816 }
817}
818
819pub struct MoveGeneratorIterator {
820 move_generator: MoveGenerator,
821 index: usize,
822 last_index: usize,
823}
824
825impl Iterator for MoveGeneratorIterator {
826 type Item = Move;
827
828 fn size_hint(&self) -> (usize, Option<usize>) {
829 let len = self.move_generator.len();
830 (len, Some(len))
831 }
832
833 fn next(&mut self) -> Option<Move> {
834 let square_and_bitboard_array_len = self.move_generator.square_and_bitboard_array.len();
836 if self.index >= square_and_bitboard_array_len {
837 return None;
838 }
839 if self.index != self.last_index {
840 while !self.move_generator.from_bitboard_iterator_mask.contains(
841 get_item_unchecked_mut!(self.move_generator.square_and_bitboard_array, self.index)
842 .square,
843 ) {
844 self.index += 1;
845 if self.index >= square_and_bitboard_array_len {
846 return None;
847 }
848 }
849 self.last_index = self.index;
850 }
851 let square_and_bitboard =
852 get_item_unchecked_mut!(self.move_generator.square_and_bitboard_array, self.index);
853
854 if !self
855 .move_generator
856 .from_bitboard_iterator_mask
857 .contains(square_and_bitboard.square)
858 || (square_and_bitboard.bitboard & self.move_generator.to_bitboard_iterator_mask)
859 .is_empty()
860 {
861 None
863 } else if square_and_bitboard.promotion {
864 let dest = unsafe {
865 (square_and_bitboard.bitboard & self.move_generator.to_bitboard_iterator_mask)
866 .to_square_unchecked()
867 };
868
869 let result = unsafe {
871 Move::new_unchecked(
872 square_and_bitboard.square,
873 dest,
874 Some(*get_item_unchecked!(
875 @internal
876 PROMOTION_PIECES,
877 self.move_generator.promotion_index
878 )),
879 )
880 };
881 self.move_generator.promotion_index += 1;
882 if self.move_generator.promotion_index >= NUM_PROMOTION_PIECES {
883 square_and_bitboard.bitboard ^= dest.to_bitboard();
884 self.move_generator.promotion_index = 0;
885 if (square_and_bitboard.bitboard & self.move_generator.to_bitboard_iterator_mask)
886 .is_empty()
887 {
888 self.index += 1;
889 }
890 }
891 Some(result)
892 } else {
893 let dest = unsafe {
895 (square_and_bitboard.bitboard & self.move_generator.to_bitboard_iterator_mask)
896 .to_square_unchecked()
897 };
898
899 square_and_bitboard.bitboard ^= dest.to_bitboard();
900 if (square_and_bitboard.bitboard & self.move_generator.to_bitboard_iterator_mask)
901 .is_empty()
902 {
903 self.index += 1;
904 }
905 Some(unsafe { Move::new_unchecked(square_and_bitboard.square, dest, None) })
906 }
907 }
908}
909
910impl ExactSizeIterator for MoveGeneratorIterator {
911 #[inline]
912 fn len(&self) -> usize {
913 self.move_generator.len()
914 }
915}
916
917impl IntoIterator for MoveGenerator {
918 type Item = Move;
919 type IntoIter = MoveGeneratorIterator;
920
921 fn into_iter(self) -> Self::IntoIter {
922 MoveGeneratorIterator {
923 move_generator: self,
924 index: 0,
925 last_index: usize::MAX,
926 }
927 }
928}