use std::num::ParseIntError;
use crate::{
piece::{Color, PieceType},
Bitboard, Square,
};
use thiserror::Error;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct FEN {
pub pieces: [[Bitboard; 6]; 2],
pub active_color: Color,
pub en_passant_target: Option<Square>,
pub halfmove_clock: u16,
pub fullmove_number: u16,
}
#[derive(Error, Debug)]
pub enum FENParseError {
#[error("not enough parts in FEN notation")]
NotEnoughParts,
#[error("invalid piece placement")]
PiecePlacement,
#[error("invalid active color")]
ActiveColor,
#[error("invalid halfmove clock")]
HalfmoveClock(#[from] ParseIntError),
#[error("invalid fullmove number")]
FullmoveNumber,
}
pub fn parse(fen: &str) -> Result<FEN, FENParseError> {
let fen = fen.trim().replace("/", "");
let mut parts = fen.split(" ");
assert!(fen.split(" ").clone().count() == 6);
let pieces = parse_piece_placement(parts.next().unwrap())?;
let active_color = parse_active_color(parts.next().unwrap())?;
let _ = parts.next();
let _ = parts.next();
let (halfmove_clock, fullmove_number) = parse_moves(parts.take(2))?;
Ok(FEN {
pieces,
active_color,
en_passant_target: None,
halfmove_clock,
fullmove_number,
})
}
fn parse_moves(
mut take: std::iter::Take<std::str::Split<'_, &str>>,
) -> Result<(u16, u16), FENParseError> {
Ok((
take.next()
.ok_or_else(|| FENParseError::FullmoveNumber)?
.parse::<u16>()?,
take.next()
.ok_or_else(|| FENParseError::FullmoveNumber)?
.parse::<u16>()?,
))
}
fn parse_active_color(color: &str) -> Result<Color, FENParseError> {
match color {
"b" => Ok(Color::Black),
"w" => Ok(Color::White),
_ => Err(FENParseError::ActiveColor),
}
}
fn parse_piece_placement(notation: &str) -> Result<[[Bitboard; 6]; 2], FENParseError> {
let mut offset = 0;
let mut pieces_bb = [
[
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
],
[
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
Bitboard(0),
],
];
for n in notation.chars() {
if n.is_digit(10) {
offset += n
.to_digit(10)
.ok_or_else(|| FENParseError::PiecePlacement)?;
} else {
match n {
'p' => {
pieces_bb[Color::Black as usize][PieceType::Pawn as usize] |=
Bitboard(1_u64 << offset);
}
'P' => {
pieces_bb[Color::White as usize][PieceType::Pawn as usize] |=
Bitboard(1_u64 << offset);
}
'r' => {
pieces_bb[Color::Black as usize][PieceType::Rook as usize] |=
Bitboard(1_u64 << offset);
}
'R' => {
pieces_bb[Color::White as usize][PieceType::Rook as usize] |=
Bitboard(1_u64 << offset);
}
'n' => {
pieces_bb[Color::Black as usize][PieceType::Knight as usize] |=
Bitboard(1_u64 << offset);
}
'N' => {
pieces_bb[Color::White as usize][PieceType::Knight as usize] |=
Bitboard(1_u64 << offset);
}
'b' => {
pieces_bb[Color::Black as usize][PieceType::Bishop as usize] |=
Bitboard(1_u64 << offset);
}
'B' => {
pieces_bb[Color::White as usize][PieceType::Bishop as usize] |=
Bitboard(1_u64 << offset);
}
'q' => {
pieces_bb[Color::Black as usize][PieceType::Queen as usize] |=
Bitboard(1_u64 << offset);
}
'Q' => {
pieces_bb[Color::White as usize][PieceType::Queen as usize] |=
Bitboard(1_u64 << offset);
}
'k' => {
pieces_bb[Color::Black as usize][PieceType::King as usize] |=
Bitboard(1_u64 << offset);
}
'K' => {
pieces_bb[Color::White as usize][PieceType::King as usize] |=
Bitboard(1_u64 << offset);
}
_ => return Err(FENParseError::PiecePlacement),
}
offset += 1;
}
}
pieces_bb[0]
.iter_mut()
.for_each(|p| *p = Bitboard(p.0.swap_bytes()));
pieces_bb[1]
.iter_mut()
.for_each(|p| *p = Bitboard(p.0.swap_bytes()));
Ok(pieces_bb)
}