bitstackchess 0.1.1

A bitboard‐based chess game engine with 10 × u128 move history
Documentation
//! CastlingLogic provides helpers to check/execute castling.
//! It only updates PieceMapping and occupied bitboard.

use crate::board::PieceMapping;

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

impl CastlingLogic {
    /// Return true if White can castle kingside given current mapping/occupied.
    pub fn can_white_kingside(mapping: &PieceMapping, occupied: u64) -> bool {
        mapping.piece_square[15] == Some(4) // White king on e1=4
            && mapping.piece_square[9] == Some(7) // White rook (kingside) on h1=7, PID=9
            && (occupied & ((1u64 << 5) | (1u64 << 6))) == 0 // f1=5, g1=6 must be empty
    }

    /// Execute white kingside castling: king(15) 4→6, rook(9) 7→5.
    pub fn do_white_kingside(mapping: &mut PieceMapping, occupied: &mut u64) {
        // King: e1=4 → g1=6
        mapping.square_piece[4] = None;
        mapping.square_piece[6] = Some(15);
        mapping.piece_square[15] = Some(6);
        *occupied &= !(1u64 << 4);
        *occupied |= 1u64 << 6;

        // Rook: h1=7 → f1=5, PID=9
        mapping.square_piece[7] = None;
        mapping.square_piece[5] = Some(9);
        mapping.piece_square[9] = Some(5);
        *occupied &= !(1u64 << 7);
        *occupied |= 1u64 << 5;
    }

    /// Return true if White can castle queenside.
    pub fn can_white_queenside(mapping: &PieceMapping, occupied: u64) -> bool {
        mapping.piece_square[15] == Some(4) // king on e1
            && mapping.piece_square[8] == Some(0) // rook on a1=0, PID=8
            && (occupied & ((1u64 << 1) | (1u64 << 2) | (1u64 << 3))) == 0 // b1=1, c1=2, d1=3 empty
    }

    /// Execute white queenside castling: king(15) 4→2, rook(8) 0→3.
    pub fn do_white_queenside(mapping: &mut PieceMapping, occupied: &mut u64) {
        // King: e1=4 → c1=2
        mapping.square_piece[4] = None;
        mapping.square_piece[2] = Some(15);
        mapping.piece_square[15] = Some(2);
        *occupied &= !(1u64 << 4);
        *occupied |= 1u64 << 2;

        // Rook: a1=0 → d1=3, PID=8
        mapping.square_piece[0] = None;
        mapping.square_piece[3] = Some(8);
        mapping.piece_square[8] = Some(3);
        *occupied &= !(1u64 << 0);
        *occupied |= 1u64 << 3;
    }

    /// Return true if Black can castle kingside.
    pub fn can_black_kingside(mapping: &PieceMapping, occupied: u64) -> bool {
        mapping.piece_square[31] == Some(60) // Black king on e8=60
            && mapping.piece_square[25] == Some(63) // Black rook (kingside) on h8=63, PID=25
            && (occupied & ((1u64 << 61) | (1u64 << 62))) == 0 // f8=61, g8=62 empty
    }

    /// Execute black kingside castling: king(31) 60→62, rook(25) 63→61.
    pub fn do_black_kingside(mapping: &mut PieceMapping, occupied: &mut u64) {
        // King: e8=60 → g8=62
        mapping.square_piece[60] = None;
        mapping.square_piece[62] = Some(31);
        mapping.piece_square[31] = Some(62);
        *occupied &= !(1u64 << 60);
        *occupied |= 1u64 << 62;

        // Rook: h8=63 → f8=61, PID=25
        mapping.square_piece[63] = None;
        mapping.square_piece[61] = Some(25);
        mapping.piece_square[25] = Some(61);
        *occupied &= !(1u64 << 63);
        *occupied |= 1u64 << 61;
    }

    /// Return true if Black can castle queenside.
    pub fn can_black_queenside(mapping: &PieceMapping, occupied: u64) -> bool {
        mapping.piece_square[31] == Some(60) // king on e8
            && mapping.piece_square[24] == Some(56) // rook on a8=56, PID=24
            && (occupied & ((1u64 << 57) | (1u64 << 58) | (1u64 << 59))) == 0 // b8=57, c8=58, d8=59 empty
    }

    /// Execute black queenside castling: king(31) 60→58, rook(24) 56→59.
    pub fn do_black_queenside(mapping: &mut PieceMapping, occupied: &mut u64) {
        // King: e8=60 → c8=58
        mapping.square_piece[60] = None;
        mapping.square_piece[58] = Some(31);
        mapping.piece_square[31] = Some(58);
        *occupied &= !(1u64 << 60);
        *occupied |= 1u64 << 58;

        // Rook: a8=56 → d8=59, PID=24
        mapping.square_piece[56] = None;
        mapping.square_piece[59] = Some(24);
        mapping.piece_square[24] = Some(59);
        *occupied &= !(1u64 << 56);
        *occupied |= 1u64 << 59;
    }
}

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

    #[test]
    fn white_kingside_castle_check() {
        let mut mapping = PieceMapping::new_empty();
        let mut occupied: u64 = 0;
        // Place White king at e1=4 and rook at h1=7
        mapping.place_piece(15, encode_square(0, 4)); // White king PID=15
        mapping.place_piece(9, encode_square(0, 7));  // White rook (kingside) PID=9
        occupied |= (1u64 << 4) | (1u64 << 7);

        assert!(CastlingLogic::can_white_kingside(&mapping, occupied));
        CastlingLogic::do_white_kingside(&mut mapping, &mut occupied);
        assert_eq!(mapping.piece_square[15], Some(6)); // king now on g1=6
        assert_eq!(mapping.piece_square[9], Some(5));  // rook PID=9 → f1=5
    }

    #[test]
    fn black_queenside_castle_check() {
        let mut mapping = PieceMapping::new_empty();
        let mut occupied: u64 = 0;
        // Place Black king at e8=60 and rook at a8=56
        mapping.place_piece(31, encode_square(7, 4)); // Black king PID=31
        mapping.place_piece(24, encode_square(7, 0)); // Black queenside rook PID=24
        occupied |= (1u64 << 60) | (1u64 << 56);

        assert!(CastlingLogic::can_black_queenside(&mapping, occupied));
        CastlingLogic::do_black_queenside(&mut mapping, &mut occupied);
        assert_eq!(mapping.piece_square[31], Some(58)); // king PID=31 → c8=58
        assert_eq!(mapping.piece_square[24], Some(59)); // rook PID=24 → d8=59
    }
}