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// }