Compare commits
4 commits
d4a1955c0f
...
a4aa4ae1e4
Author | SHA1 | Date | |
---|---|---|---|
Bruno BELANYI | a4aa4ae1e4 | ||
Bruno BELANYI | 353271f427 | ||
Bruno BELANYI | 08f010ed32 | ||
Bruno BELANYI | f4764f2174 |
|
@ -8,9 +8,10 @@ pub struct ChessBoardBuilder {
|
|||
// Same fields as [ChessBoard].
|
||||
castle_rights: [CastleRights; Color::NUM_VARIANTS],
|
||||
en_passant: Option<Square>,
|
||||
half_move_clock: u8,
|
||||
total_plies: u32,
|
||||
half_move_clock: 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,13 +41,13 @@ 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
|
||||
}
|
||||
|
||||
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<ChessBoardBuilder> 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<ChessBoardBuilder> 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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct ChessBoard {
|
|||
/// `Some(target_square)` if a double-step move was made.
|
||||
en_passant: Option<Square>,
|
||||
/// 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<Square>,
|
||||
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::<ChessBoard>::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 = {
|
||||
|
|
15
src/fen.rs
15
src/fen.rs
|
@ -139,24 +139,21 @@ 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::<Square>::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::<u8>()
|
||||
.parse::<_>()
|
||||
.map_err(|_| FenError::InvalidFen)?;
|
||||
builder.with_half_move_clock(half_move_clock);
|
||||
|
||||
let full_move_counter = full_move_counter
|
||||
.parse::<u32>()
|
||||
.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;
|
||||
|
@ -175,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.
|
||||
|
|
Loading…
Reference in a new issue