bitstackchess 0.1.1

A bitboard‐based chess game engine with 10 × u128 move history
Documentation
//! EnPassantLogic checks/executes en passant captures.
//! It only updates PieceMapping and occupied bitboard, returning the captured piece.

use crate::board::PieceMapping;

/// A stateless struct containing only static helper methods.
pub struct EnPassantLogic;

impl EnPassantLogic {
    /// If side=0 (White) two-step from src→dest, return dest−8 as ep target.  
    /// If side=1 (Black) two-step, return dest+8. Otherwise None.
    pub fn compute_ep_target(src: u8, dest: u8, side: u8) -> Option<u8> {
        let src_rank = src >> 3;
        let dst_rank = dest >> 3;
        if side == 0 && dst_rank == src_rank + 2 {
            Some(dest - 8)
        } else if side == 1 && dst_rank + 2 == src_rank {
            Some(dest + 8)
        } else {
            None
        }
    }

    /// If a pawn is moving diagonally into `dest == ep_target`, remove the opposing pawn
    /// “behind” that square, update mapping & occupied, and return the captured pid.
    pub fn do_ep_capture(
        mapping: &mut PieceMapping,
        occupied: &mut u64,
        ep_target: u8,
        side: u8,
    ) -> Option<u8> {
        let captured_sq = if side == 0 { ep_target - 8 } else { ep_target + 8 };
        if let Some(opp_pid) = mapping.who_on_square(captured_sq) {
            mapping.remove_piece(opp_pid);
            *occupied &= !(1u64 << (captured_sq as u64));
            return Some(opp_pid);
        }
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::board::{encode_piece, encode_square, PieceMapping};

    #[test]
    fn compute_and_do_ep_capture() {
        let mut mapping = PieceMapping::new_empty();
        let mut occupied: u64 = 0;

        // Setup: White pawn on e5=encode_square(4,4)=36, Black pawn on d5=encode_square(4,3)=35.
        let white_pawn = encode_piece(0, true, 4, 0);
        let black_pawn = encode_piece(1, true, 3, 0);
        mapping.place_piece(white_pawn, encode_square(4, 4));
        mapping.place_piece(black_pawn, encode_square(4, 3));
        occupied |= (1u64 << 36) | (1u64 << 35);

        // Compute EP target for Black’s two-step: suppose Black had just done d7→d5 (rank 6→4).
        let ep_target = EnPassantLogic::compute_ep_target(encode_square(6, 3), encode_square(4, 3), 1);
        assert_eq!(ep_target, Some(encode_square(5, 3))); // d6= encode_square(5,3)=43

        // Now White pawn moves e5→d6=43 to capture en passant
        mapping.remove_piece(white_pawn);
        mapping.place_piece(white_pawn, encode_square(5, 3));
        occupied &= !(1u64 << 36);
        occupied |= 1u64 << 43;

        // Do ep capture:
        let captured = EnPassantLogic::do_ep_capture(
            &mut mapping,
            &mut occupied,
            ep_target.unwrap(),
            0,
        );
        assert_eq!(captured, Some(black_pawn));
        assert!(mapping.who_on_square(encode_square(4, 3)).is_none()); // d5 must be empty
    }
}