diff --git a/src/gamemanager/legal_moves.rs b/src/gamemanager/legal_moves.rs index 1b0ffdd..3c43727 100644 --- a/src/gamemanager/legal_moves.rs +++ b/src/gamemanager/legal_moves.rs @@ -54,6 +54,7 @@ impl GameManager { for mv in &pslm { debug_assert!(mv.1 != mv.2); + // Create a new GameManager here. let mut modified_gm = { match color { @@ -88,7 +89,6 @@ impl GameManager { } } - // TODO: Test mask and king intersection. let enemy_attacked = modified_gm.attacked_by( &tbl, match color { @@ -101,19 +101,19 @@ impl GameManager { use Square::*; match mv.3 { KingCastle => { - if color == Color::Black - && (E8.to_u64() | F8.to_u64() | G8.to_u64()) & enemy_attacked != 0 - || color == Color::White - && (E1.to_u64() | F1.to_u64() | G1.to_u64()) & enemy_attacked != 0 + if (color == Color::Black + && ((E8.to_u64() | F8.to_u64() | G8.to_u64()) & enemy_attacked) != 0) + || (color == Color::White + && ((E1.to_u64() | F1.to_u64() | G1.to_u64()) & enemy_attacked) != 0) { continue; // We don't want this move! } } QueenCastle => { if color == Color::Black - && (E8.to_u64() | D8.to_u64() | C8.to_u64()) & enemy_attacked != 0 + && ((E8.to_u64() | D8.to_u64() | C8.to_u64()) & enemy_attacked) != 0 || color == Color::White - && (E1.to_u64() | D1.to_u64() | C1.to_u64()) & enemy_attacked != 0 + && ((E1.to_u64() | D1.to_u64() | C1.to_u64()) & enemy_attacked) != 0 { continue; // Ditto. } @@ -161,7 +161,7 @@ impl GameManager { // - castling_rights if it is a KingCastle or QueenCastle move. // // THESE SHOULD HOLD FOR ALL CODE BLOCKS BELOW! CHECK THIS IN REVIEW, VERY CAREFULLY, OR ELSE! - match piecetype { + let retval = match piecetype { PieceType::Bishop => match movetype { MoveType::QuietMove => GameManager { bitboard: BitBoard { @@ -187,7 +187,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -197,12 +196,20 @@ impl GameManager { // NOTE: Color-dependent logic. let new_castling_rights = if from == Square::A8 { CastlingRecord { - black: CastlingRights::Kingside, + black: match self.castling_rights.black { + CastlingRights::Both => CastlingRights::Kingside, + CastlingRights::Queenside => CastlingRights::Neither, + _ => self.castling_rights.black, + }, ..self.castling_rights } } else if from == Square::H8 { CastlingRecord { - black: CastlingRights::Queenside, + black: match self.castling_rights.black { + CastlingRights::Both => CastlingRights::Queenside, + CastlingRights::Kingside => CastlingRights::Neither, + _ => self.castling_rights.black, + }, ..self.castling_rights } } else { @@ -217,7 +224,6 @@ impl GameManager { }, castling_rights: new_castling_rights, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -235,7 +241,6 @@ impl GameManager { }, castling_rights: new_castling_rights, en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -257,7 +262,6 @@ impl GameManager { ..self.castling_rights }, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::QueenCastle => GameManager { @@ -272,7 +276,6 @@ impl GameManager { ..self.castling_rights }, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -287,9 +290,11 @@ impl GameManager { queens_white: self.bitboard.queens_white & !to_square, ..self.bitboard }, - castling_rights: self.castling_rights.clone(), + castling_rights: CastlingRecord { + black: CastlingRights::Neither, + ..self.castling_rights + }, en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -298,9 +303,11 @@ impl GameManager { king_black: (self.bitboard.king_black ^ from.to_u64()) | to.to_u64(), ..self.bitboard }, - castling_rights: self.castling_rights.clone(), + castling_rights: CastlingRecord { + black: CastlingRights::Neither, + ..self.castling_rights + }, en_passant_target: self.en_passant_target.clone(), - ..*self }, _ => unreachable!("Kings will never make another type of move."), @@ -313,7 +320,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -331,7 +337,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -347,7 +352,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::RPromotion => GameManager { @@ -358,7 +362,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::NPromotion => GameManager { @@ -369,7 +372,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::QPromotion => GameManager { @@ -380,7 +382,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, // On a promotion capture to X delete all enemy pieces @@ -400,7 +401,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -419,7 +419,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -438,7 +437,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -457,7 +455,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -487,7 +484,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -516,7 +512,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: String::from(target_coord), - ..*self } } @@ -527,7 +522,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -561,7 +555,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -578,7 +571,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -587,7 +579,28 @@ impl GameManager { PieceType::Super => { unreachable!("We will never generate pseudolegal Super moves.") } - } + }; + + assert!( + retval.bitboard.king_black.is_power_of_two(), + "{} {:?} {:?} {:#X} {:#X}\n", + retval.bitboard.to_string(), + retval.castling_rights.black, + retval.castling_rights.white, + retval.bitboard.king_black, + retval.bitboard.king_white + ); + assert!( + retval.bitboard.king_white.is_power_of_two(), + "{} {:?} {:?} {:#X} {:#X}\n", + retval.bitboard.to_string(), + retval.castling_rights.black, + retval.castling_rights.white, + retval.bitboard.king_black, + retval.bitboard.king_white + ); + + retval } /// Extracted from the large match block above. @@ -598,6 +611,8 @@ impl GameManager { from: Square, to: Square, ) -> GameManager { + assert!(self.bitboard.king_black.is_power_of_two()); + assert!(self.bitboard.king_white.is_power_of_two()); // For each type of piece, there are at least two moves that can be made, Quiet and Capture. // A quiet move needs to update only a handful of things, namely the move clocks, bitboard // of the moving piece type, and black_to_move boolean. @@ -611,7 +626,7 @@ impl GameManager { // - castling_rights if it is a KingCastle or QueenCastle move. // // THESE SHOULD HOLD FOR ALL CODE BLOCKS BELOW! CHECK THIS IN REVIEW, VERY CAREFULLY, OR ELSE! - match piecetype { + let retval = match piecetype { PieceType::Bishop => match movetype { MoveType::QuietMove => GameManager { bitboard: BitBoard { @@ -648,12 +663,20 @@ impl GameManager { // NOTE: Color-dependent logic. let new_castling_rights = if from == Square::A1 { CastlingRecord { - white: CastlingRights::Kingside, + white: match self.castling_rights.white { + CastlingRights::Both => CastlingRights::Kingside, + CastlingRights::Queenside => CastlingRights::Neither, + _ => self.castling_rights.white, + }, ..self.castling_rights } } else if from == Square::H1 { CastlingRecord { - white: CastlingRights::Queenside, + white: match self.castling_rights.white { + CastlingRights::Both => CastlingRights::Queenside, + CastlingRights::Kingside => CastlingRights::Neither, + _ => self.castling_rights.white, + }, ..self.castling_rights } } else { @@ -668,7 +691,6 @@ impl GameManager { }, castling_rights: new_castling_rights, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -686,7 +708,6 @@ impl GameManager { }, castling_rights: new_castling_rights, en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -708,7 +729,6 @@ impl GameManager { ..self.castling_rights }, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::QueenCastle => GameManager { @@ -723,7 +743,6 @@ impl GameManager { ..self.castling_rights }, en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -738,9 +757,11 @@ impl GameManager { queens_black: self.bitboard.queens_black & !to_square, ..self.bitboard }, - castling_rights: self.castling_rights.clone(), + castling_rights: CastlingRecord { + white: CastlingRights::Neither, + ..self.castling_rights + }, en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -749,9 +770,11 @@ impl GameManager { king_white: (self.bitboard.king_white ^ from.to_u64()) | to.to_u64(), ..self.bitboard }, - castling_rights: self.castling_rights.clone(), + castling_rights: CastlingRecord { + white: CastlingRights::Neither, + ..self.castling_rights + }, en_passant_target: self.en_passant_target.clone(), - ..*self }, _ => unreachable!("Kings will never make another type of move."), @@ -764,7 +787,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -782,7 +804,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -798,7 +819,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::RPromotion => GameManager { @@ -809,7 +829,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::NPromotion => GameManager { @@ -820,7 +839,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::QPromotion => GameManager { @@ -831,7 +849,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, // On a promotion capture to X delete all enemy pieces @@ -851,7 +868,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -870,7 +886,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -889,7 +904,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -908,7 +922,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -938,7 +951,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -967,7 +979,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: String::from(target_coord), - ..*self } } @@ -978,7 +989,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -995,7 +1005,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -1012,7 +1021,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self }, MoveType::Capture => { @@ -1029,7 +1037,6 @@ impl GameManager { }, castling_rights: self.castling_rights.clone(), en_passant_target: self.en_passant_target.clone(), - ..*self } } @@ -1038,7 +1045,28 @@ impl GameManager { PieceType::Super => { unreachable!("We will never generate pseudolegal Super moves.") } - } + }; + + assert!( + retval.bitboard.king_black.is_power_of_two(), + "{} {:?} {:?} {:#X} {:#X}\n", + retval.bitboard.to_string(), + retval.castling_rights.black, + retval.castling_rights.white, + retval.bitboard.king_black, + retval.bitboard.king_white + ); + assert!( + retval.bitboard.king_white.is_power_of_two(), + "{} {:?} {:?} {:#X} {:#X}\n", + retval.bitboard.to_string(), + retval.castling_rights.black, + retval.castling_rights.white, + retval.bitboard.king_black, + retval.bitboard.king_white + ); + + retval } } diff --git a/src/gamemanager/legal_moves/perft.rs b/src/gamemanager/legal_moves/perft.rs index 0838d7e..d8dc12f 100644 --- a/src/gamemanager/legal_moves/perft.rs +++ b/src/gamemanager/legal_moves/perft.rs @@ -1,5 +1,3 @@ -use crate::types::Square; - use super::{GameManager, MoveTable, NoArc}; use rayon::prelude::*; @@ -16,21 +14,11 @@ pub fn perft(depth: u16, maxdepth: u16, gm: GameManager, tbl: &NoArc) pub fn printing_perft(depth: u16, maxdepth: u16, gm: GameManager, tbl: &NoArc) { for mv in gm.legal_moves(tbl) { - if mv.1 == Square::E1 && mv.2 == Square::C1 { - println!("--- MOVE E1C1:"); - printing_perft(depth + 1, maxdepth, mv.4, tbl); - println!("--- END E1C1."); - } else if mv.1 == Square::A6 && mv.2 == Square::B5 { - println!("--- MOVE A6B5:"); - printing_perft(depth + 1, maxdepth, mv.4, tbl); - println!("--- END A6B5."); - } else { - println!( - "{}{}: {}", - mv.1.to_str().to_ascii_lowercase(), - mv.2.to_str().to_ascii_lowercase(), - perft(depth, maxdepth, mv.4, tbl) - ) - } + println!( + "{}{}: {}", + mv.1.to_str().to_ascii_lowercase(), + mv.2.to_str().to_ascii_lowercase(), + perft(depth + 1, maxdepth, mv.4, tbl) + ) } } diff --git a/src/gamemanager/mod.rs b/src/gamemanager/mod.rs index 61e7521..03212c0 100644 --- a/src/gamemanager/mod.rs +++ b/src/gamemanager/mod.rs @@ -144,6 +144,7 @@ impl GameManager { } /// Returns a bitmask of all the pieces attacked by the given color on this GameManager's state. + /// TODO, BUG: Needs to be more careful of pawn moves. Pawns' forward moves cannot capture. pub fn attacked_by(&self, tbl: &NoArc, color: Color) -> u64 { let moves = pseudolegal_moves( color, @@ -155,8 +156,12 @@ impl GameManager { tbl, ); + use crate::types::MoveType::*; + use crate::types::PieceType::*; + moves .iter() + .filter(|mv| mv.0 != Pawn || mv.3 != QuietMove) // Isn't a pawn or isn't a pawn's quiet move. .map(|(_, _, to, _)| to.to_u64()) .fold(0_u64, |acc, v| acc | v) } diff --git a/src/gamemanager/pseudolegal_moves/kings.rs b/src/gamemanager/pseudolegal_moves/kings.rs index fe42f40..79161de 100644 --- a/src/gamemanager/pseudolegal_moves/kings.rs +++ b/src/gamemanager/pseudolegal_moves/kings.rs @@ -54,7 +54,7 @@ pub fn pseudolegal_king_moves( movetable: &NoArc, ) -> Vec { let mut king_pseudo_legal_moves = Vec::new(); - + assert_eq!(king_locations.len(), 1); match color { Color::Black => { for king in king_locations { @@ -152,7 +152,7 @@ pub fn pseudolegal_king_moves( // Add castling moves to the normal moves. - // MAGIC NUMBERS: These are masks for the squares between E8 and the corners. + // MAGIC NUMBERS: These are masks for the squares between E1 and the corners. // Conditions: // - Correct side castling rights // - No friendly pieces in the way diff --git a/src/gamemanager/pseudolegal_moves/mod.rs b/src/gamemanager/pseudolegal_moves/mod.rs index 1119ed4..3d53a8d 100644 --- a/src/gamemanager/pseudolegal_moves/mod.rs +++ b/src/gamemanager/pseudolegal_moves/mod.rs @@ -14,7 +14,7 @@ use crate::{ bitboard::BitBoard, gamemanager::GameManager, movetable::{noarc::NoArc, MoveTable}, - types::{CastlingRecord, Color, Move}, + types::{CastlingRecord, Color, Move, Square}, }; /// Returns a [`Vec`] of pseudolegal moves encoded as a [`Move`](Move) type, @@ -31,6 +31,8 @@ pub fn pseudolegal_moves( ) -> Vec { let mut pseudolegal_moves: Vec = Vec::new(); + //assert!(bitboard.king_black.is_power_of_two()); + //assert!(bitboard.king_white.is_power_of_two()); match color { Color::Black => { // For each black piece on the board, obtain its possible moves. @@ -211,5 +213,13 @@ pub fn pseudolegal_moves( // pseudolegal_moves.len() // ); + // for mv in &pseudolegal_moves { + // if mv.1 == Square::E1 && mv.2 == Square::G1 { + // println!("--- MOVE E1G1:"); + // println!("{:?} {:?}", mv.0, mv.3); + // println!("--- END E1G1."); + // } + // } + pseudolegal_moves } diff --git a/src/main.rs b/src/main.rs index 488ade4..d2a4aeb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,9 +15,11 @@ mod ucimanager; fn main() { let tbl = noarc::NoArc::new(MoveTable::default()); - let gm = GameManager::default(); + let gm = GameManager::from_fen_str( + "r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", + ); println!("Searched {} nodes.", perft(0, 6, gm, &tbl)); - //printing_perft(0, 2, gm, &tbl); + //printing_perft(0, 4, gm, &tbl); } #[cfg(test)] diff --git a/src/types.rs b/src/types.rs index 0cfc545..7223fd3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -408,7 +408,7 @@ impl Square { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum MoveType { QuietMove, DoublePawnPush,