From 4efb48244f1334658e278b2d2e287d79c26ce223 Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Sun, 6 Oct 2024 21:28:11 -0400 Subject: [PATCH] feat: make en-passant moves --- src/lib.rs | 20 ++++++++++++++++---- src/movegen.rs | 27 ++++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3999641..d6aa55e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,7 +168,7 @@ impl Square { 'f' => 5, 'g' => 6, 'h' => 7, - _ => {return Err(SquareError::InvalidCharacter(bytes[0] as char))} + _ => return Err(SquareError::InvalidCharacter(bytes[0] as char)), }; if let Some(row) = (bytes[1] as char).to_digit(10) { Square::from_row_col(row as usize - 1, col as usize) @@ -187,7 +187,10 @@ mod tests { let test_cases = [("a1", 0), ("a8", 56), ("h1", 7), ("h8", 63)]; for (sqr, idx) in test_cases { assert_eq!(Square::try_from(idx).unwrap().to_algebraic(), sqr); - assert_eq!(Square::from_algebraic(sqr).unwrap(), Square::try_from(idx).unwrap()); + assert_eq!( + Square::from_algebraic(sqr).unwrap(), + Square::try_from(idx).unwrap() + ); } } } @@ -339,6 +342,10 @@ pub struct BoardState { turn: Color, } +/// Piece missing where there should be one. +#[derive(Debug)] +struct NoPieceError; + impl BoardState { /// Get mutable reference to a player. fn pl_mut(&mut self, col: Color) -> &mut Player { @@ -352,12 +359,17 @@ impl BoardState { *self.mail.sq_mut(idx) = Some(pc); } - /// Delete the piece in a location, if it exists. - fn del_piece(&mut self, idx: Square) { + /// Delete the piece in a location. + /// + /// Returns an error if there is no piece in the location. + fn del_piece(&mut self, idx: Square) -> Result<(), NoPieceError> { if let Some(pc) = *self.mail.sq_mut(idx) { let pl = self.pl_mut(pc.col); pl.board(pc.into()).off_idx(idx); *self.mail.sq_mut(idx) = None; + Ok(()) + } else { + Err(NoPieceError) } } diff --git a/src/movegen.rs b/src/movegen.rs index 8c89960..8f73afb 100644 --- a/src/movegen.rs +++ b/src/movegen.rs @@ -98,7 +98,7 @@ impl Move { pc_asserts!(pc_src, self); debug_assert_eq!(pc_src.pc, Piece::Pawn); - node.pos.del_piece(self.src); + node.pos.del_piece(self.src).expect("Move source should have piece."); node.pos.set_piece( self.dest, ColPiece { @@ -127,12 +127,22 @@ impl Move { Color::Black => { self.src.0 - BOARD_WIDTH }, }; node.pos.ep_square = Some( - // TODO: fix the en passant targetsquare Square::try_from(new_idx) .expect("En-passant target should be valid."), ) } else { node.pos.ep_square = None; + if pc_dest.is_none() && src_col != dest_col { + // we took en passant + debug_assert!(src_row.abs_diff(dest_row) == 1); + debug_assert_eq!(self.dest, old_pos.ep_square.unwrap()); + // square to actually capture at + let ep_capture = Square::try_from(match pc_src.col { + Color::White => { self.dest.0 - BOARD_WIDTH }, + Color::Black => { self.dest.0 + BOARD_WIDTH }, + }).expect("En-passant capture square should be valid."); + node.pos.del_piece(ep_capture).expect("En-passant capture square should have piece."); + } } } else { node.pos.half_moves += 1; @@ -168,7 +178,7 @@ impl Move { } } - node.pos.del_piece(self.src); + node.pos.del_piece(self.src).expect("Move source should have piece."); node.pos.set_piece(self.dest, pc_src); } } @@ -331,6 +341,17 @@ mod tests { ("b2b1q", "4k1N1/8/8/8/8/8/8/1q2K3 w - - 0 2"), ], ), + // en passant test + ( + "k7/4p3/8/3P4/3p4/8/4P3/K7 w - - 0 1", + vec![ + ("e2e4", "k7/4p3/8/3P4/3pP3/8/8/K7 b - e3 0 1"), + ("d4e3", "k7/4p3/8/3P4/8/4p3/8/K7 w - - 0 2"), + ("a1b1", "k7/4p3/8/3P4/8/4p3/8/1K6 b - - 1 2"), + ("e7e5", "k7/8/8/3Pp3/8/4p3/8/1K6 w - e6 0 3"), + ("d5e6", "k7/8/4P3/8/8/4p3/8/1K6 b - - 0 3"), + ], + ), ]; for (i, test_case) in test_cases.iter().enumerate() {