diff --git a/src/board/chess_board/builder.rs b/src/board/chess_board/builder.rs index d82ec18..d16c881 100644 --- a/src/board/chess_board/builder.rs +++ b/src/board/chess_board/builder.rs @@ -8,9 +8,9 @@ pub struct ChessBoardBuilder { // Same fields as [ChessBoard]. castle_rights: [CastleRights; Color::NUM_VARIANTS], en_passant: Option, - half_move_clock: u8, + half_move_clock: u32, side: Color, - // 1-based, A turn is *two* half-moves (i.e: both players have played). + // 1-based, a turn is *two* half-moves (i.e: both players have played). turn_count: u32, } @@ -41,7 +41,7 @@ impl ChessBoardBuilder { self } - pub fn with_half_move_clock(&mut self, clock: u8) -> &mut Self { + pub fn with_half_move_clock(&mut self, clock: u32) -> &mut Self { self.half_move_clock = clock; self } diff --git a/src/board/chess_board/error.rs b/src/board/chess_board/error.rs index 4265de0..7b570a4 100644 --- a/src/board/chess_board/error.rs +++ b/src/board/chess_board/error.rs @@ -23,6 +23,8 @@ pub enum InvalidError { ErroneousCombinedOccupancy, /// Half-move clock is higher than total number of plies. HalfMoveClockTooHigh, + /// The total plie count does not match the current player. + IncoherentPlieCount, } impl std::fmt::Display for InvalidError { @@ -45,6 +47,7 @@ impl std::fmt::Display for InvalidError { "The pre-computed combined occupancy boards does not match the other boards." } Self::HalfMoveClockTooHigh => "Half-move clock is higher than total number of plies.", + Self::IncoherentPlieCount => "The total plie count does not match the current player.", }; write!(f, "{}", error_msg) } diff --git a/src/board/chess_board/mod.rs b/src/board/chess_board/mod.rs index 879aba6..9ceb673 100644 --- a/src/board/chess_board/mod.rs +++ b/src/board/chess_board/mod.rs @@ -24,7 +24,7 @@ pub struct ChessBoard { /// `Some(target_square)` if a double-step move was made. en_passant: Option, /// The number of half-turns without either a pawn push or capture. - half_move_clock: u8, // Should never go higher than 50. + half_move_clock: u32, // Should *probably* never go higher than 100. /// The number of half-turns so far. total_plies: u32, // Should be plenty. /// The current player turn. @@ -36,7 +36,7 @@ pub struct ChessBoard { pub struct NonReversibleState { castle_rights: [CastleRights; Color::NUM_VARIANTS], en_passant: Option, - half_move_clock: u8, // Should never go higher than 50. + half_move_clock: u32, // Should *probably* never go higher than 100. } impl ChessBoard { @@ -105,7 +105,7 @@ impl ChessBoard { /// Return the number of half-turns without either a pawn push or a capture. #[inline(always)] - pub fn half_move_clock(&self) -> u8 { + pub fn half_move_clock(&self) -> u32 { self.half_move_clock } @@ -208,8 +208,13 @@ impl ChessBoard { /// Validate the state of the board. Return Err([InvalidError]) if an issue is found. pub fn validate(&self) -> Result<(), InvalidError> { + // The current plie count should be odd on white's turn, and vice-versa. + if self.total_plies() % 2 != self.current_player().index() as u32 { + return Err(InvalidError::IncoherentPlieCount); + } + // Make sure the clocks are in agreement. - if u32::from(self.half_move_clock()) > self.total_plies() { + if self.half_move_clock() > self.total_plies() { return Err(InvalidError::HalfMoveClockTooHigh); } @@ -428,6 +433,22 @@ mod test { assert!(default_position.is_valid()); } + #[test] + fn invalid_incoherent_plie_count() { + let position = { + let mut builder = ChessBoardBuilder::new(); + builder[Square::E1] = Some((Piece::King, Color::White)); + builder[Square::E8] = Some((Piece::King, Color::Black)); + let mut board = TryInto::::try_into(builder).unwrap(); + board.total_plies = 1; + board + }; + assert_eq!( + position.validate().err().unwrap(), + InvalidError::IncoherentPlieCount, + ); + } + #[test] fn invalid_half_moves_clock() { let res = { diff --git a/src/fen.rs b/src/fen.rs index a7a6825..90f6dd1 100644 --- a/src/fen.rs +++ b/src/fen.rs @@ -139,20 +139,19 @@ impl FromFen for ChessBoard { builder.with_castle_rights(castle_rights[color.index()], color); } - let side = Color::from_fen(side_to_move)?; - builder.with_current_player(side); + builder.with_current_player(FromFen::from_fen(side_to_move)?); - if let Some(square) = Option::::from_fen(en_passant_square)? { + if let Some(square) = FromFen::from_fen(en_passant_square)? { builder.with_en_passant(square); }; let half_move_clock = half_move_clock - .parse::() + .parse::<_>() .map_err(|_| FenError::InvalidFen)?; builder.with_half_move_clock(half_move_clock); let full_move_counter = full_move_counter - .parse::() + .parse::<_>() .map_err(|_| FenError::InvalidFen)?; builder.with_turn_count(full_move_counter); @@ -173,7 +172,7 @@ impl FromFen for ChessBoard { file += digit.to_digit(10).unwrap() as usize; continue; } - _ => Piece::from_fen(&c.to_string())?, + _ => FromFen::from_fen(&c.to_string())?, }; // Only need to worry about underflow since those are `usize` values.