chess/movegen.rs
1use crate::mailbox;
2use crate::position;
3
4pub const MOVE_VEC_SIZE: usize = 27; // max number of squares a queen can possibly move to is 27
5pub type Offset = [i32; 8];
6
7pub const PAWN_OFFSET: Offset = [0, 0, 0, 0, 0, 0, 0, 0];
8pub const KNIGHT_OFFSET: Offset = [-21, -19, -12, -8, 8, 12, 19, 21];
9pub const BISHOP_OFFSET: Offset = [-11, -9, 9, 11, 0, 0, 0, 0];
10pub const ROOK_OFFSET: Offset = [-10, -1, 1, 10, 0, 0, 0, 0];
11pub const QUEEN_KING_OFFSET: Offset = [-11, -10, -9, -1, 1, 9, 10, 11];
12
13pub const PROMOTION_PIECE_TYPES: [PieceType; 4] = [
14 PieceType::Knight,
15 PieceType::Bishop,
16 PieceType::Rook,
17 PieceType::Queen,
18];
19
20// starting indexes for castling logic
21// TODO refactor castling logic so its not hardcoded, to allow for fischer random positions
22pub const LONG_BLACK_ROOK_START: usize = 0;
23pub const SHORT_BLACK_ROOK_START: usize = 7;
24pub const LONG_WHITE_ROOK_START: usize = 56;
25pub const SHORT_WHITE_ROOK_START: usize = 63;
26pub const BLACK_KING_START: usize = 4;
27pub const WHITE_KING_START: usize = 60;
28
29// from and to are out of bounds
30pub const NULL_MOVE: Move = Move {
31 piece: Piece {
32 ptype: PieceType::King,
33 pcolour: PieceColour::White,
34 }, // dummy piece
35 from: usize::MAX,
36 to: usize::MAX,
37 move_type: MoveType::None,
38};
39// from and to are out of bounds
40pub const NULL_SHORT_MOVE: ShortMove = ShortMove {
41 from: u8::MAX,
42 to: u8::MAX,
43 promotion_ptype: None,
44};
45
46#[derive(Debug, PartialEq, Eq, Clone, Copy)]
47pub enum PieceType {
48 Pawn,
49 Knight,
50 Bishop,
51 Rook,
52 Queen,
53 King,
54}
55
56#[derive(PartialEq, Eq, Debug, Clone, Copy)]
57pub enum PieceColour {
58 White,
59 Black,
60}
61impl core::ops::Not for PieceColour {
62 type Output = Self;
63 fn not(self) -> Self::Output {
64 match self {
65 Self::White => Self::Black,
66 Self::Black => Self::White,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub struct Piece {
73 pub pcolour: PieceColour,
74 pub ptype: PieceType,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum Square {
79 Piece(Piece),
80 Empty,
81}
82
83#[derive(Debug, Clone, Copy, Default)]
84pub struct MovegenFlags {
85 pub white_castle_short: bool,
86 pub white_castle_long: bool,
87 pub black_castle_short: bool,
88 pub black_castle_long: bool,
89 pub en_passant: Option<usize>,
90 pub polyglot_en_passant: Option<usize>,
91}
92
93#[derive(Debug, PartialEq, Eq, Clone, Copy)]
94pub struct Move {
95 pub piece: Piece,
96 pub from: usize,
97 pub to: usize,
98 pub move_type: MoveType,
99}
100impl Move {
101 pub const fn short_move(&self) -> ShortMove {
102 ShortMove {
103 from: self.from as u8,
104 to: self.to as u8,
105 promotion_ptype: match self.move_type {
106 MoveType::Promotion(ptype, _) => Some(ptype),
107 _ => None,
108 },
109 }
110 }
111}
112
113// struct that stores enough information to identify any full sized move
114#[derive(Debug, PartialEq, Eq, Clone, Copy)]
115pub struct ShortMove {
116 pub from: u8,
117 pub to: u8,
118 pub promotion_ptype: Option<PieceType>,
119}
120
121impl PartialEq<ShortMove> for Move {
122 fn eq(&self, other: &ShortMove) -> bool {
123 let result = self.from == other.from as usize && self.to == other.to as usize;
124 // promotion checks
125 if let Some(other_ptype) = other.promotion_ptype {
126 if let MoveType::Promotion(self_ptype, _) = self.move_type {
127 return result && self_ptype == other_ptype;
128 }
129 }
130 result
131 }
132}
133
134impl PartialEq<Move> for ShortMove {
135 fn eq(&self, other: &Move) -> bool {
136 let result = self.from as usize == other.from && self.to as usize == other.to;
137 // promotion checks
138 if let Some(self_ptype) = self.promotion_ptype {
139 if let MoveType::Promotion(other_ptype, _) = other.move_type {
140 return result && self_ptype == other_ptype;
141 }
142 }
143 result
144 }
145}
146
147#[derive(Debug, PartialEq, Eq, Clone, Copy)]
148pub enum CastleSide {
149 Short,
150 Long,
151}
152
153#[derive(Debug, PartialEq, Eq, Clone, Copy)]
154pub struct CastleMove {
155 pub rook_from: usize,
156 pub rook_to: usize,
157 pub king_squares: (usize, usize, usize),
158}
159impl CastleMove {
160 pub const fn get_castle_side(&self) -> CastleSide {
161 // simple test to differentiate between long and short castles by looking at the idx of the rook_from square
162 if self.rook_from == LONG_BLACK_ROOK_START || self.rook_from == LONG_WHITE_ROOK_START {
163 CastleSide::Long
164 } else {
165 CastleSide::Short
166 }
167 }
168}
169
170#[derive(Debug, PartialEq, Eq, Clone, Copy)]
171pub enum MoveType {
172 EnPassant(usize),
173 Promotion(PieceType, Option<PieceType>),
174 Castle(CastleMove),
175 DoublePawnPush,
176 PawnPush,
177 Capture(PieceType),
178 Normal,
179 None, // used to represent null move, or moves that are only used in generating defend map, and are not actually possible to play
180}
181impl MoveType {
182 #[inline]
183 pub const fn is_capture(&self) -> bool {
184 matches!(
185 self,
186 Self::Capture(_) | Self::EnPassant(_) | Self::Promotion(_, Some(_))
187 )
188 }
189}
190
191pub trait MoveMap {
192 fn add_move(&mut self, _: &Move);
193}
194
195#[inline(always)]
196fn pawn_promotion(
197 mv_map: &mut dyn MoveMap,
198 i: usize,
199 piece: Piece,
200 mv: i32,
201 capture: Option<PieceType>,
202) {
203 for ptype in PROMOTION_PIECE_TYPES {
204 mv_map.add_move(
205 &(Move {
206 piece,
207 from: i,
208 to: mv as usize,
209 move_type: MoveType::Promotion(ptype, capture),
210 }),
211 );
212 }
213}
214
215#[inline(always)]
216fn is_square_empty(pos: &position::Pos64, i: usize) -> bool {
217 // unsafe { return pos.get_unchecked(i) == &Square::Empty }
218 pos[i] == Square::Empty
219}
220
221#[inline(always)]
222const fn mb_get_pawn_push_offset(piece: Piece) -> i32 {
223 match piece.pcolour {
224 PieceColour::White => -10,
225 PieceColour::Black => 10,
226 }
227}
228
229#[inline(always)]
230const fn mb_get_pawn_attack_offset(piece: Piece) -> [i32; 2] {
231 const WHITE_ATTACK_OFFSET: [i32; 2] = [-9, -11];
232 const BLACK_ATTACK_OFFSET: [i32; 2] = [9, 11];
233 match piece.pcolour {
234 PieceColour::White => WHITE_ATTACK_OFFSET,
235 PieceColour::Black => BLACK_ATTACK_OFFSET,
236 }
237}
238
239#[inline(always)]
240const fn mb_get_offset(piece: Piece) -> Offset {
241 match piece.ptype {
242 PieceType::Pawn => PAWN_OFFSET, // not used
243 PieceType::Knight => KNIGHT_OFFSET,
244 PieceType::Bishop => BISHOP_OFFSET,
245 PieceType::Rook => ROOK_OFFSET,
246 PieceType::Queen => QUEEN_KING_OFFSET,
247 PieceType::King => QUEEN_KING_OFFSET,
248 }
249}
250
251#[inline(always)]
252const fn get_slide(piece: Piece) -> bool {
253 match piece.ptype {
254 PieceType::Pawn | PieceType::Knight | PieceType::King => false,
255 PieceType::Bishop | PieceType::Rook | PieceType::Queen => true,
256 }
257}
258
259#[inline(always)]
260const fn pawn_is_promotion_square(i: i32, piece: Piece) -> bool {
261 match piece.pcolour {
262 PieceColour::White => i <= 7,
263 PieceColour::Black => i >= 56,
264 }
265}
266
267#[inline(always)]
268const fn pawn_is_starting_rank(i: usize, piece: Piece) -> bool {
269 match piece.pcolour {
270 PieceColour::White => i < 56 && i > 47,
271 PieceColour::Black => i < 16 && i > 7,
272 }
273}
274
275// generates moves for the piece at index i, only checks legality regarding where pieces could possibly move to
276// doesnt account for discovered king checks after the move
277pub(crate) fn movegen(
278 pos: &position::Pos64,
279 movegen_flags: &MovegenFlags,
280 piece: Piece,
281 i: usize,
282 defending: bool,
283 mv_map: &mut dyn MoveMap,
284) {
285 // Move gen for pawns
286 if piece.ptype == PieceType::Pawn {
287 // mailbox offset for moving pawns straight up
288 let push_offset = mb_get_pawn_push_offset(piece);
289
290 // pawn push logic, only when defending is false, as pawn pushes are non-controlling moves
291 if !defending {
292 // closure that pushes move to mv_map, if move is valid and the mv square is empty
293 // returns true if it pushes successfully
294 let mut push_if_empty = |mv: i32, mvtype: MoveType| -> bool {
295 // check mv is valid
296 if mv >= 0 {
297 // push mv if the square is empty
298 if is_square_empty(pos, mv as usize) {
299 if pawn_is_promotion_square(mv, piece) {
300 pawn_promotion(mv_map, i, piece, mv, None);
301 } else {
302 mv_map.add_move(
303 &(Move {
304 piece,
305 from: i,
306 to: mv as usize,
307 move_type: mvtype,
308 }),
309 );
310 }
311 true
312 } else {
313 false
314 }
315 } else {
316 false // also return false if mv is out of bounds
317 }
318 };
319
320 let mv_single_push = mailbox::next_mailbox_number(i, push_offset);
321 let is_empty = push_if_empty(mv_single_push, MoveType::PawnPush);
322
323 // check if pawn is still on starting rank
324 let is_starting = pawn_is_starting_rank(i, piece);
325
326 // if pawn is on starting square and the first square above it was empty
327 // this is to prevent the pawn from jumping over a piece on it's first move
328 if is_starting && is_empty {
329 let mv_double_push = mailbox::next_mailbox_number(i, push_offset * 2);
330 // again, only pushing if the second square above is empty
331 push_if_empty(mv_double_push, MoveType::DoublePawnPush);
332 }
333 }
334
335 // Attacking/Defending moves for pawns
336 let attack_offset = mb_get_pawn_attack_offset(piece);
337
338 for j in attack_offset {
339 let mv = mailbox::next_mailbox_number(i, j);
340 if mv >= 0 {
341 let mv_square = &pos[mv as usize];
342 match mv_square {
343 Square::Piece(mv_square_piece) => {
344 if piece.pcolour != mv_square_piece.pcolour || defending {
345 if pawn_is_promotion_square(mv, piece) && !defending {
346 // no need to do this if defending, we can just do it once below regardless
347 pawn_promotion(mv_map, i, piece, mv, Some(mv_square_piece.ptype));
348 } else {
349 mv_map.add_move(
350 &(Move {
351 piece,
352 from: i,
353 to: mv as usize,
354 move_type: MoveType::Capture(mv_square_piece.ptype),
355 }),
356 );
357 }
358 }
359 }
360 Square::Empty => {
361 // no pawn promotion logic, as you cant promote diagonally to an empty square. Only needed for defend map
362 if defending {
363 mv_map.add_move(
364 &(Move {
365 piece,
366 from: i,
367 to: mv as usize,
368 move_type: MoveType::None, // not a real move, only a defensive one
369 }),
370 );
371 }
372 }
373 }
374 }
375 }
376 // en passant captures, checking pawns left and right
377 // also dont check for promotion, as a pawn cannot en passant to the back rank
378 if !defending && movegen_flags.en_passant.is_some() {
379 let attack_en_passant_offset = [-1, 1];
380 let en_passant_mv = movegen_flags.en_passant.unwrap();
381 for j in attack_en_passant_offset {
382 let mv = mailbox::next_mailbox_number(i, j);
383 if mv == (en_passant_mv as i32) {
384 // check if square above this is empty
385 let mv_above = mailbox::next_mailbox_number(mv as usize, push_offset);
386 if mv_above >= 0 && is_square_empty(pos, mv_above as usize) {
387 mv_map.add_move(
388 &(Move {
389 piece,
390 from: i,
391 to: mv_above as usize,
392 move_type: MoveType::EnPassant(mv as usize),
393 }),
394 );
395 }
396 } else {
397 continue;
398 }
399 }
400 }
401 } else {
402 // move gen for other pieces
403 for j in mb_get_offset(piece) {
404 // end of offsets
405 if j == 0 {
406 break;
407 }
408
409 let mut mv = mailbox::next_mailbox_number(i, j);
410 let mut slide_idx = j;
411
412 while mv >= 0 {
413 let mv_square = &pos[mv as usize];
414 match mv_square {
415 Square::Piece(mv_square_piece) => {
416 if piece.pcolour != mv_square_piece.pcolour || defending {
417 mv_map.add_move(
418 &(Move {
419 piece,
420 from: i,
421 to: mv as usize,
422 move_type: MoveType::Capture(mv_square_piece.ptype),
423 }),
424 );
425 }
426 break; // break the slide after encountering a piece
427 }
428 Square::Empty => {
429 mv_map.add_move(
430 &(Move {
431 piece,
432 from: i,
433 to: mv as usize,
434 move_type: MoveType::Normal,
435 }),
436 );
437 }
438 }
439 // is piece a sliding type
440 if get_slide(piece) {
441 slide_idx += j;
442 mv = mailbox::next_mailbox_number(i, slide_idx);
443
444 continue;
445 } else {
446 break;
447 } // continue through rest of offsets
448 }
449 }
450 }
451
452 // finally, movegen for castling
453 if piece.ptype == PieceType::King
454 && !defending
455 && ((piece.pcolour == PieceColour::White && i == WHITE_KING_START)
456 || (piece.pcolour == PieceColour::Black && i == BLACK_KING_START))
457 {
458 // no need to check mailbox, or check if an index is out of bounds
459 // as we check that the king is on its starting square
460
461 if (piece.pcolour == PieceColour::White && movegen_flags.white_castle_short)
462 || (piece.pcolour == PieceColour::Black && movegen_flags.black_castle_short)
463 {
464 let short_mv_through_idx = i + 1;
465 let short_mv_to_idx = i + 2;
466 let short_rook_start_idx = i + 3;
467 let short_rook_end_idx = short_mv_through_idx;
468 if matches!(&pos[short_mv_through_idx], Square::Empty)
469 && matches!(&pos[short_mv_to_idx], Square::Empty)
470 {
471 mv_map.add_move(
472 &(Move {
473 piece,
474 from: i,
475 to: short_mv_to_idx,
476 move_type: MoveType::Castle(CastleMove {
477 rook_from: short_rook_start_idx,
478 rook_to: short_rook_end_idx,
479 king_squares: (i, short_mv_through_idx, short_mv_to_idx),
480 }),
481 }),
482 );
483 }
484 }
485 if (piece.pcolour == PieceColour::White && movegen_flags.white_castle_long)
486 || (piece.pcolour == PieceColour::Black && movegen_flags.black_castle_long)
487 {
488 let long_mv_through_idx = i - 1;
489 let long_mv_to_idx = i - 2;
490 let long_mv_past_idx = i - 3; // sqaure not in kings path but still needs to be empty for rook
491 let long_rook_start_idx = i - 4;
492 let long_rook_end_idx = long_mv_through_idx;
493 if matches!(&pos[long_mv_through_idx], Square::Empty)
494 && matches!(&pos[long_mv_to_idx], Square::Empty)
495 && matches!(&pos[long_mv_past_idx], Square::Empty)
496 {
497 mv_map.add_move(
498 &(Move {
499 piece,
500 from: i,
501 to: long_mv_to_idx,
502 move_type: MoveType::Castle(CastleMove {
503 rook_from: long_rook_start_idx,
504 rook_to: long_rook_end_idx,
505 king_squares: (i, long_mv_through_idx, long_mv_to_idx),
506 }),
507 }),
508 );
509 }
510 }
511 }
512}
513
514pub fn movegen_in_check(pos: &position::Pos64, king_idx: usize) -> bool {
515 let king_colour = if let Square::Piece(p) = pos[king_idx] {
516 p.pcolour
517 } else {
518 unreachable!("king_idx does not contain a king....")
519 }; // just give the correct value please and we dont need to panic
520 for (i, s) in pos.iter().enumerate() {
521 match s {
522 Square::Piece(piece) => {
523 if piece.pcolour != king_colour {
524 // Move gen for pawns
525 if piece.ptype == PieceType::Pawn {
526 // Defending moves for pawns
527 let attack_offset = mb_get_pawn_attack_offset(*piece);
528
529 for j in attack_offset {
530 let mv = mailbox::next_mailbox_number(i, j);
531 if mv >= 0 {
532 if (mv as usize) == king_idx {
533 return true;
534 } else {
535 continue;
536 }
537 }
538 }
539 } else {
540 // move gen for other pieces
541 for j in mb_get_offset(*piece) {
542 // end of offsets
543 if j == 0 {
544 break;
545 }
546
547 let mut mv = mailbox::next_mailbox_number(i, j);
548 let mut slide_idx = j;
549
550 while mv >= 0 {
551 let mv_square = &pos[mv as usize];
552 match mv_square {
553 Square::Piece(_) => {
554 if (mv as usize) == king_idx {
555 return true;
556 } else {
557 break; // break the slide after encountering a piece
558 }
559 }
560 Square::Empty => {
561 if (mv as usize) == king_idx {
562 return true;
563 }
564 }
565 }
566 // is piece a sliding type
567 if get_slide(*piece) {
568 slide_idx += j;
569 mv = mailbox::next_mailbox_number(i, slide_idx);
570
571 continue;
572 } else {
573 break;
574 } // continue through rest of offsets
575 }
576 }
577 }
578 } else {
579 continue;
580 }
581 }
582 Square::Empty => {
583 continue;
584 }
585 }
586 }
587 false
588}
589
590// UNUSED
591// pub fn movegen_pos<'a>(
592// pos: &'a position::Pos64,
593// movegen_flags: &MovegenFlags,
594// attacking_side: PieceColour,
595// attack_map: &'a mut dyn MoveMap,
596// defend_map: &'a mut dyn MoveMap,
597// ) {
598// for (i, s) in pos.iter().enumerate() {
599// match s {
600// Square::Empty => {
601// continue;
602// }
603// Square::Piece(piece) => {
604// let mv_map = if piece.pcolour == attacking_side {
605// &mut *attack_map
606// } else {
607// &mut *defend_map
608// };
609// let defending = piece.pcolour != attacking_side;
610// // Move gen for pawns
611// if piece.ptype == PieceType::Pawn {
612// // mailbox offset for moving pawns straight up
613// let push_offset = mb_get_pawn_push_offset(piece);
614
615// // pawn push logic, only when defending is false, as pawn pushes are non-controlling moves
616// if !defending {
617// // closure that pushes move to mv_map, if move is valid and the mv square is empty
618// // returns true if it pushes successfully
619// let mut push_if_empty = |mv: i32, mvtype: MoveType| -> bool {
620// // check mv is valid
621// if mv >= 0 {
622// // push mv if the square is empty
623// if is_square_empty(pos, mv as usize) {
624// if !defending && pawn_is_promotion_square(mv, piece) {
625// pawn_promotion(mv_map, i, piece, mv);
626// } else {
627// mv_map.add_move(
628// &(Move {
629// piece: *piece,
630// from: i,
631// to: mv as usize,
632// move_type: mvtype,
633// }),
634// );
635// }
636// true
637// } else {
638// false
639// }
640// } else {
641// false // also return false if mv is out of bounds
642// }
643// };
644
645// let mv_single_push = mailbox::next_mailbox_number(i, push_offset);
646// let is_empty = push_if_empty(mv_single_push, MoveType::PawnPush);
647
648// // check if pawn is still on starting rank
649// let is_starting = pawn_is_starting_rank(i, piece);
650
651// // if pawn is on starting square and the first square above it was empty
652// // this is to prevent the pawn from jumping over a piece on it's first move
653// if is_starting && is_empty {
654// let mv_double_push = mailbox::next_mailbox_number(i, push_offset * 2);
655// // again, only pushing if the second square above is empty
656// push_if_empty(mv_double_push, MoveType::DoublePawnPush);
657// }
658// }
659
660// // Attacking/Defending moves for pawns
661// let attack_offset = mb_get_pawn_attack_offset(piece);
662
663// for j in attack_offset {
664// let mv = mailbox::next_mailbox_number(i, j);
665// if mv >= 0 {
666// let mv_square = &pos[mv as usize];
667// match mv_square {
668// Square::Piece(mv_square_piece) => {
669// if piece.pcolour != mv_square_piece.pcolour || defending {
670// if pawn_is_promotion_square(mv, piece) && !defending {
671// // no need to do this if defending, we can just do it once below regardless
672// pawn_promotion(mv_map, i, piece, mv);
673// } else {
674// mv_map.add_move(
675// &(Move {
676// piece: *piece,
677// from: i,
678// to: mv as usize,
679// move_type: MoveType::Capture(
680// mv_square_piece.ptype,
681// ),
682// }),
683// );
684// }
685// }
686// }
687// Square::Empty => {
688// // no pawn promotion logic, as you cant promote diagonally to an empty square. Only needed for defend map
689// if defending {
690// mv_map.add_move(
691// &(Move {
692// piece: *piece,
693// from: i,
694// to: mv as usize,
695// move_type: MoveType::None, // not a real move, only a defensive one
696// }),
697// );
698// }
699// }
700// }
701// }
702// }
703// // en passant captures, checking pawns left and right
704// // also dont check for promotion, as a pawn cannot en passant to the back rank
705// if !defending && movegen_flags.en_passant.is_some() {
706// let attack_en_passant_offset = [-1, 1];
707// let en_passant_mv = movegen_flags.en_passant.unwrap();
708// for j in attack_en_passant_offset {
709// let mv = mailbox::next_mailbox_number(i, j);
710// if mv == (en_passant_mv as i32) {
711// // check if square above this is empty
712// let mv_above =
713// mailbox::next_mailbox_number(mv as usize, push_offset);
714// if mv_above >= 0 && is_square_empty(pos, mv_above as usize) {
715// mv_map.add_move(
716// &(Move {
717// piece: *piece,
718// from: i,
719// to: mv_above as usize,
720// move_type: MoveType::EnPassant(mv as usize),
721// }),
722// );
723// }
724// } else {
725// continue;
726// }
727// }
728// }
729// } else {
730// // move gen for other pieces
731// for j in mb_get_offset(piece) {
732// // end of offsets
733// if j == 0 {
734// break;
735// }
736
737// let mut mv = mailbox::next_mailbox_number(i, j);
738// let mut slide_idx = j;
739
740// while mv >= 0 {
741// let mv_square = &pos[mv as usize];
742// match mv_square {
743// Square::Piece(mv_square_piece) => {
744// if piece.pcolour != mv_square_piece.pcolour || defending {
745// mv_map.add_move(
746// &(Move {
747// piece: *piece,
748// from: i,
749// to: mv as usize,
750// move_type: MoveType::Capture(mv_square_piece.ptype),
751// }),
752// );
753// }
754// break; // break the slide after encountering a piece
755// }
756// Square::Empty => {
757// mv_map.add_move(
758// &(Move {
759// piece: *piece,
760// from: i,
761// to: mv as usize,
762// move_type: MoveType::Normal,
763// }),
764// );
765// }
766// }
767// // is piece a sliding type
768// if get_slide(piece) {
769// slide_idx += j;
770// mv = mailbox::next_mailbox_number(i, slide_idx);
771
772// continue;
773// } else {
774// break;
775// } // continue through rest of offsets
776// }
777// }
778// }
779
780// // finally, movegen for castling
781// if piece.ptype == PieceType::King
782// && !defending
783// && ((piece.pcolour == PieceColour::White && i == WHITE_KING_START)
784// || (piece.pcolour == PieceColour::Black && i == BLACK_KING_START))
785// {
786// // no need to check mailbox, or check if an index is out of bounds
787// // as we check that the king is on its starting square
788
789// if (piece.pcolour == PieceColour::White && movegen_flags.white_castle_short)
790// || (piece.pcolour == PieceColour::Black && movegen_flags.black_castle_short)
791// {
792// let short_mv_through_idx = i + 1;
793// let short_mv_to_idx = i + 2;
794// let short_rook_start_idx = i + 3;
795// let short_rook_end_idx = short_mv_through_idx;
796// if matches!(&pos[short_mv_through_idx], Square::Empty)
797// && matches!(&pos[short_mv_to_idx], Square::Empty)
798// {
799// mv_map.add_move(
800// &(Move {
801// piece: *piece,
802// from: i,
803// to: short_mv_to_idx,
804// move_type: MoveType::Castle(CastleMove {
805// rook_from: short_rook_start_idx,
806// rook_to: short_rook_end_idx,
807// king_squares: (i, short_mv_through_idx, short_mv_to_idx),
808// }),
809// }),
810// );
811// }
812// }
813// if (piece.pcolour == PieceColour::White && movegen_flags.white_castle_long)
814// || (piece.pcolour == PieceColour::Black && movegen_flags.black_castle_long)
815// {
816// let long_mv_through_idx = i - 1;
817// let long_mv_to_idx = i - 2;
818// let long_mv_past_idx = i - 3; // sqaure not in kings path but still needs to be empty for rook
819// let long_rook_start_idx = i - 4;
820// let long_rook_end_idx = long_mv_through_idx;
821// if matches!(&pos[long_mv_through_idx], Square::Empty)
822// && matches!(&pos[long_mv_to_idx], Square::Empty)
823// && matches!(&pos[long_mv_past_idx], Square::Empty)
824// {
825// mv_map.add_move(
826// &(Move {
827// piece: *piece,
828// from: i,
829// to: long_mv_to_idx,
830// move_type: MoveType::Castle(CastleMove {
831// rook_from: long_rook_start_idx,
832// rook_to: long_rook_end_idx,
833// king_squares: (i, long_mv_through_idx, long_mv_to_idx),
834// }),
835// }),
836// );
837// }
838// }
839// }
840// }
841// }
842// }
843// }