diff --git a/src/board/chess_board/builder.rs b/src/board/chess_board/builder.rs index 8221d92..d82ec18 100644 --- a/src/board/chess_board/builder.rs +++ b/src/board/chess_board/builder.rs @@ -9,8 +9,9 @@ pub struct ChessBoardBuilder { castle_rights: [CastleRights; Color::NUM_VARIANTS], en_passant: Option, half_move_clock: u8, - total_plies: u32, side: Color, + // 1-based, A turn is *two* half-moves (i.e: both players have played). + turn_count: u32, } impl ChessBoardBuilder { @@ -20,8 +21,8 @@ impl ChessBoardBuilder { castle_rights: [CastleRights::NoSide; 2], en_passant: Default::default(), half_move_clock: Default::default(), - total_plies: Default::default(), side: Color::White, + turn_count: 1, } } @@ -45,8 +46,8 @@ impl ChessBoardBuilder { self } - pub fn with_total_plies(&mut self, plies: u32) -> &mut Self { - self.total_plies = plies; + pub fn with_turn_count(&mut self, count: u32) -> &mut Self { + self.turn_count = count; self } @@ -90,8 +91,8 @@ impl TryFrom for ChessBoard { castle_rights, en_passant, half_move_clock, - total_plies, side, + turn_count, } = builder; for square in Square::iter() { @@ -103,6 +104,8 @@ impl TryFrom for ChessBoard { combined_occupancy |= square; } + let total_plies = (turn_count - 1) * 2 + if side == Color::White { 0 } else { 1 }; + let board = ChessBoard { piece_occupancy, color_occupancy, @@ -146,7 +149,7 @@ mod test { builder .with_half_move_clock(board.half_move_clock()) - .with_total_plies(board.total_plies()) + .with_turn_count(board.total_plies() / 2 + 1) .with_current_player(board.current_player()); builder diff --git a/src/board/chess_board/error.rs b/src/board/chess_board/error.rs index e6ef030..4265de0 100644 --- a/src/board/chess_board/error.rs +++ b/src/board/chess_board/error.rs @@ -21,6 +21,8 @@ pub enum InvalidError { OverlappingColors, /// The pre-computed combined occupancy boards does not match the other boards. ErroneousCombinedOccupancy, + /// Half-move clock is higher than total number of plies. + HalfMoveClockTooHigh, } impl std::fmt::Display for InvalidError { @@ -42,6 +44,7 @@ impl std::fmt::Display for InvalidError { Self::ErroneousCombinedOccupancy => { "The pre-computed combined occupancy boards does not match the other boards." } + Self::HalfMoveClockTooHigh => "Half-move clock is higher than total number of plies.", }; write!(f, "{}", error_msg) } diff --git a/src/board/chess_board/mod.rs b/src/board/chess_board/mod.rs index 2a98537..879aba6 100644 --- a/src/board/chess_board/mod.rs +++ b/src/board/chess_board/mod.rs @@ -208,6 +208,11 @@ impl ChessBoard { /// Validate the state of the board. Return Err([InvalidError]) if an issue is found. pub fn validate(&self) -> Result<(), InvalidError> { + // Make sure the clocks are in agreement. + if u32::from(self.half_move_clock()) > self.total_plies() { + return Err(InvalidError::HalfMoveClockTooHigh); + } + // Don't overlap pieces. for piece in Piece::iter() { #[allow(clippy::collapsible_if)] @@ -423,6 +428,18 @@ mod test { assert!(default_position.is_valid()); } + #[test] + fn invalid_half_moves_clock() { + let res = { + let mut builder = ChessBoardBuilder::new(); + builder[Square::E1] = Some((Piece::King, Color::White)); + builder[Square::E8] = Some((Piece::King, Color::Black)); + builder.with_half_move_clock(10); + TryInto::::try_into(builder) + }; + assert_eq!(res.err().unwrap(), InvalidError::HalfMoveClockTooHigh); + } + #[test] fn invalid_overlapping_pieces() { let position = ChessBoard { @@ -679,10 +696,10 @@ mod test { let mut builder = ChessBoardBuilder::new(); builder[Square::H1] = Some((Piece::King, Color::White)); builder[Square::H8] = Some((Piece::King, Color::Black)); - for square in (File::B.into_bitboard() | File::C.into_bitboard()) { + for square in (File::B.into_bitboard() | File::C.into_bitboard()).into_iter() { builder[square] = Some((Piece::Pawn, Color::White)); } - for square in (File::F.into_bitboard() | File::G.into_bitboard()) { + for square in (File::F.into_bitboard() | File::G.into_bitboard()).into_iter() { builder[square] = Some((Piece::Pawn, Color::Black)); } TryInto::::try_into(builder) diff --git a/src/fen.rs b/src/fen.rs index 3096c95..a7a6825 100644 --- a/src/fen.rs +++ b/src/fen.rs @@ -154,9 +154,7 @@ impl FromFen for ChessBoard { let full_move_counter = full_move_counter .parse::() .map_err(|_| FenError::InvalidFen)?; - builder.with_total_plies( - (full_move_counter - 1) * 2 + if side == Color::White { 0 } else { 1 }, - ); + builder.with_turn_count(full_move_counter); { let mut rank: usize = 8;