Move 'FromFen' for 'ChessBoard' into 'fen' module
This commit is contained in:
parent
b9cc60be9c
commit
c3be661719
|
@ -1,7 +1,4 @@
|
|||
use crate::{
|
||||
fen::{FenError, FromFen},
|
||||
movegen,
|
||||
};
|
||||
use crate::movegen;
|
||||
|
||||
use super::{Bitboard, CastleRights, Color, File, Move, Piece, Rank, Square};
|
||||
|
||||
|
@ -403,94 +400,10 @@ impl Default for ChessBoard {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return a [ChessBoard] from the given FEN string.
|
||||
impl FromFen for ChessBoard {
|
||||
type Err = FenError;
|
||||
|
||||
fn from_fen(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut split = s.split_ascii_whitespace();
|
||||
|
||||
let piece_placement = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let side_to_move = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let castling_rights = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let en_passant_square = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let half_move_clock = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let full_move_counter = split.next().ok_or(FenError::InvalidFen)?;
|
||||
|
||||
let mut builder = ChessBoardBuilder::new();
|
||||
|
||||
let castle_rights = <[CastleRights; 2]>::from_fen(castling_rights)?;
|
||||
for color in Color::iter() {
|
||||
builder.with_castle_rights(castle_rights[color.index()], color);
|
||||
}
|
||||
|
||||
let side = Color::from_fen(side_to_move)?;
|
||||
builder.with_current_player(side);
|
||||
|
||||
if let Some(square) = Option::<Square>::from_fen(en_passant_square)? {
|
||||
builder.with_en_passant(square);
|
||||
};
|
||||
|
||||
let half_move_clock = half_move_clock
|
||||
.parse::<u8>()
|
||||
.map_err(|_| FenError::InvalidFen)?;
|
||||
builder.with_half_move_clock(half_move_clock);
|
||||
|
||||
let full_move_counter = full_move_counter
|
||||
.parse::<u32>()
|
||||
.map_err(|_| FenError::InvalidFen)?;
|
||||
builder.with_total_plies(
|
||||
(full_move_counter - 1) * 2 + if side == Color::White { 0 } else { 1 },
|
||||
);
|
||||
|
||||
{
|
||||
let mut rank: usize = 8;
|
||||
for rank_str in piece_placement.split('/') {
|
||||
rank -= 1;
|
||||
let mut file: usize = 0;
|
||||
for c in rank_str.chars() {
|
||||
let color = if c.is_uppercase() {
|
||||
Color::White
|
||||
} else {
|
||||
Color::Black
|
||||
};
|
||||
let piece = match c {
|
||||
digit @ '1'..='8' => {
|
||||
// Unwrap is fine since this arm is only matched by digits
|
||||
file += digit.to_digit(10).unwrap() as usize;
|
||||
continue;
|
||||
}
|
||||
_ => Piece::from_fen(&c.to_string())?,
|
||||
};
|
||||
|
||||
// Only need to worry about underflow since those are `usize` values.
|
||||
if file >= 8 || rank >= 8 {
|
||||
return Err(FenError::InvalidFen);
|
||||
};
|
||||
|
||||
let square = Square::new(File::from_index(file), Rank::from_index(rank));
|
||||
|
||||
builder[square] = Some((piece, color));
|
||||
file += 1;
|
||||
}
|
||||
// We haven't read exactly 8 files.
|
||||
if file != 8 {
|
||||
return Err(FenError::InvalidFen);
|
||||
}
|
||||
}
|
||||
// We haven't read exactly 8 ranks
|
||||
if rank != 0 {
|
||||
return Err(FenError::InvalidFen);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(builder.try_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::board::MoveBuilder;
|
||||
use crate::fen::FromFen;
|
||||
|
||||
use super::*;
|
||||
|
||||
|
|
89
src/fen.rs
89
src/fen.rs
|
@ -1,4 +1,6 @@
|
|||
use crate::board::{CastleRights, Color, File, InvalidError, Piece, Rank, Square};
|
||||
use crate::board::{
|
||||
CastleRights, ChessBoard, ChessBoardBuilder, Color, File, InvalidError, Piece, Rank, Square,
|
||||
};
|
||||
|
||||
/// A trait to mark items that can be converted from a FEN input.
|
||||
pub trait FromFen: Sized {
|
||||
|
@ -115,3 +117,88 @@ impl FromFen for Piece {
|
|||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a [ChessBoard] from the given FEN string.
|
||||
impl FromFen for ChessBoard {
|
||||
type Err = FenError;
|
||||
|
||||
fn from_fen(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut split = s.split_ascii_whitespace();
|
||||
|
||||
let piece_placement = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let side_to_move = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let castling_rights = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let en_passant_square = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let half_move_clock = split.next().ok_or(FenError::InvalidFen)?;
|
||||
let full_move_counter = split.next().ok_or(FenError::InvalidFen)?;
|
||||
|
||||
let mut builder = ChessBoardBuilder::new();
|
||||
|
||||
let castle_rights = <[CastleRights; 2]>::from_fen(castling_rights)?;
|
||||
for color in Color::iter() {
|
||||
builder.with_castle_rights(castle_rights[color.index()], color);
|
||||
}
|
||||
|
||||
let side = Color::from_fen(side_to_move)?;
|
||||
builder.with_current_player(side);
|
||||
|
||||
if let Some(square) = Option::<Square>::from_fen(en_passant_square)? {
|
||||
builder.with_en_passant(square);
|
||||
};
|
||||
|
||||
let half_move_clock = half_move_clock
|
||||
.parse::<u8>()
|
||||
.map_err(|_| FenError::InvalidFen)?;
|
||||
builder.with_half_move_clock(half_move_clock);
|
||||
|
||||
let full_move_counter = full_move_counter
|
||||
.parse::<u32>()
|
||||
.map_err(|_| FenError::InvalidFen)?;
|
||||
builder.with_total_plies(
|
||||
(full_move_counter - 1) * 2 + if side == Color::White { 0 } else { 1 },
|
||||
);
|
||||
|
||||
{
|
||||
let mut rank: usize = 8;
|
||||
for rank_str in piece_placement.split('/') {
|
||||
rank -= 1;
|
||||
let mut file: usize = 0;
|
||||
for c in rank_str.chars() {
|
||||
let color = if c.is_uppercase() {
|
||||
Color::White
|
||||
} else {
|
||||
Color::Black
|
||||
};
|
||||
let piece = match c {
|
||||
digit @ '1'..='8' => {
|
||||
// Unwrap is fine since this arm is only matched by digits
|
||||
file += digit.to_digit(10).unwrap() as usize;
|
||||
continue;
|
||||
}
|
||||
_ => Piece::from_fen(&c.to_string())?,
|
||||
};
|
||||
|
||||
// Only need to worry about underflow since those are `usize` values.
|
||||
if file >= 8 || rank >= 8 {
|
||||
return Err(FenError::InvalidFen);
|
||||
};
|
||||
|
||||
let square = Square::new(File::from_index(file), Rank::from_index(rank));
|
||||
|
||||
builder[square] = Some((piece, color));
|
||||
file += 1;
|
||||
}
|
||||
// We haven't read exactly 8 files.
|
||||
if file != 8 {
|
||||
return Err(FenError::InvalidFen);
|
||||
}
|
||||
}
|
||||
// We haven't read exactly 8 ranks
|
||||
if rank != 0 {
|
||||
return Err(FenError::InvalidFen);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(builder.try_into()?)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue