[position] Remove fen.rs
This commit is contained in:
parent
634f9e02f4
commit
f96fa79dc1
2 changed files with 0 additions and 284 deletions
|
@ -1,282 +0,0 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
|
||||||
|
|
||||||
use crate::{Position, PositionBuilder};
|
|
||||||
use chessfriend_core::{piece, Color, File, Piece, PlacedPiece, Rank, Square};
|
|
||||||
use chessfriend_moves::{Castle, EnPassant};
|
|
||||||
use std::fmt::Write;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! fen {
|
|
||||||
($fen_string:literal) => {
|
|
||||||
Position::from_fen_str($fen_string)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
pub enum ToFenError {
|
|
||||||
FmtError(std::fmt::Error),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
pub struct FromFenError;
|
|
||||||
|
|
||||||
pub trait ToFen {
|
|
||||||
type Error;
|
|
||||||
fn to_fen(&self) -> Result<String, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FromFen: Sized {
|
|
||||||
type Error;
|
|
||||||
fn from_fen_str(string: &str) -> Result<Self, Self::Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToFen for Position {
|
|
||||||
type Error = ToFenError;
|
|
||||||
|
|
||||||
fn to_fen(&self) -> Result<String, Self::Error> {
|
|
||||||
let mut fen_string = String::new();
|
|
||||||
|
|
||||||
let mut empty_squares: u8 = 0;
|
|
||||||
for rank in Rank::ALL.iter().rev() {
|
|
||||||
for file in File::ALL.iter() {
|
|
||||||
let square = Square::from_file_rank(*file, *rank);
|
|
||||||
match self.piece_on_square(square) {
|
|
||||||
Some(piece) => {
|
|
||||||
if empty_squares > 0 {
|
|
||||||
write!(fen_string, "{}", empty_squares)
|
|
||||||
.map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
empty_squares = 0;
|
|
||||||
}
|
|
||||||
write!(fen_string, "{}", piece.to_fen()?)
|
|
||||||
.map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
}
|
|
||||||
None => empty_squares += 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if empty_squares > 0 {
|
|
||||||
write!(fen_string, "{}", empty_squares).map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
empty_squares = 0;
|
|
||||||
}
|
|
||||||
if rank != &Rank::ONE {
|
|
||||||
write!(fen_string, "/").map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
write!(fen_string, " {}", self.player_to_move().to_fen()?)
|
|
||||||
.map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
|
|
||||||
let castling = [
|
|
||||||
(Color::White, Castle::KingSide),
|
|
||||||
(Color::White, Castle::QueenSide),
|
|
||||||
(Color::Black, Castle::KingSide),
|
|
||||||
(Color::Black, Castle::QueenSide),
|
|
||||||
]
|
|
||||||
.map(|(color, castle)| {
|
|
||||||
let can_castle = self.player_has_right_to_castle(color, castle);
|
|
||||||
if !can_castle {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
match (color, castle) {
|
|
||||||
(Color::White, Castle::KingSide) => "K",
|
|
||||||
(Color::White, Castle::QueenSide) => "Q",
|
|
||||||
(Color::Black, Castle::KingSide) => "k",
|
|
||||||
(Color::Black, Castle::QueenSide) => "q",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.concat();
|
|
||||||
|
|
||||||
write!(
|
|
||||||
fen_string,
|
|
||||||
" {}",
|
|
||||||
if castling.len() > 0 { &castling } else { "-" }
|
|
||||||
)
|
|
||||||
.map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
|
|
||||||
write!(
|
|
||||||
fen_string,
|
|
||||||
" {}",
|
|
||||||
self.en_passant()
|
|
||||||
.map_or("-".to_string(), |ep| ep.target_square().to_string())
|
|
||||||
)
|
|
||||||
.map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
|
|
||||||
write!(fen_string, " {}", self.ply_counter()).map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
write!(fen_string, " {}", self.move_number()).map_err(|err| ToFenError::FmtError(err))?;
|
|
||||||
|
|
||||||
Ok(fen_string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToFen for Color {
|
|
||||||
type Error = ToFenError;
|
|
||||||
|
|
||||||
fn to_fen(&self) -> Result<String, Self::Error> {
|
|
||||||
match self {
|
|
||||||
Color::White => Ok("w".to_string()),
|
|
||||||
Color::Black => Ok("b".to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToFen for Piece {
|
|
||||||
type Error = ToFenError;
|
|
||||||
|
|
||||||
fn to_fen(&self) -> Result<String, Self::Error> {
|
|
||||||
let ascii: char = self.to_ascii();
|
|
||||||
Ok(String::from(match self.color() {
|
|
||||||
Color::White => ascii.to_ascii_uppercase(),
|
|
||||||
Color::Black => ascii.to_ascii_lowercase(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToFen for PlacedPiece {
|
|
||||||
type Error = ToFenError;
|
|
||||||
|
|
||||||
fn to_fen(&self) -> Result<String, Self::Error> {
|
|
||||||
Ok(self.piece().to_fen()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromFen for Position {
|
|
||||||
type Error = FromFenError;
|
|
||||||
|
|
||||||
fn from_fen_str(string: &str) -> Result<Self, Self::Error> {
|
|
||||||
let mut builder = PositionBuilder::empty();
|
|
||||||
|
|
||||||
let mut fields = string.split(" ");
|
|
||||||
|
|
||||||
let placements = fields.next().ok_or(FromFenError)?;
|
|
||||||
let ranks = placements.split("/");
|
|
||||||
|
|
||||||
for (rank, pieces) in Rank::ALL.iter().rev().zip(ranks) {
|
|
||||||
let mut files = File::ALL.iter();
|
|
||||||
for ch in pieces.chars() {
|
|
||||||
if let Some(skip) = ch.to_digit(10) {
|
|
||||||
// TODO: Use advance_by() when it's available.
|
|
||||||
for _ in 0..skip {
|
|
||||||
files.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = files.next().ok_or(FromFenError)?;
|
|
||||||
let piece = Piece::from_fen_str(&ch.to_string())?;
|
|
||||||
|
|
||||||
builder.place_piece(PlacedPiece::new(
|
|
||||||
piece,
|
|
||||||
Square::from_file_rank(*file, *rank),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug_assert_eq!(files.next(), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let player_to_move = Color::from_fen_str(fields.next().ok_or(FromFenError)?)?;
|
|
||||||
builder.to_move(player_to_move);
|
|
||||||
|
|
||||||
let castling_rights = fields.next().ok_or(FromFenError)?;
|
|
||||||
if castling_rights == "-" {
|
|
||||||
builder.no_castling_rights();
|
|
||||||
} else {
|
|
||||||
for ch in castling_rights.chars() {
|
|
||||||
match ch {
|
|
||||||
'K' => builder.player_can_castle(Color::White, Castle::KingSide),
|
|
||||||
'Q' => builder.player_can_castle(Color::White, Castle::QueenSide),
|
|
||||||
'k' => builder.player_can_castle(Color::Black, Castle::KingSide),
|
|
||||||
'q' => builder.player_can_castle(Color::Black, Castle::QueenSide),
|
|
||||||
_ => return Err(FromFenError),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let en_passant_square = fields.next().ok_or(FromFenError)?;
|
|
||||||
if en_passant_square != "-" {
|
|
||||||
let square = Square::from_algebraic_str(en_passant_square).map_err(|_| FromFenError)?;
|
|
||||||
builder.en_passant(Some(EnPassant::from_target_square(square).unwrap()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let half_move_clock = fields.next().ok_or(FromFenError)?;
|
|
||||||
let half_move_clock: u16 = half_move_clock.parse().map_err(|_| FromFenError)?;
|
|
||||||
builder.ply_counter(half_move_clock);
|
|
||||||
|
|
||||||
let full_move_counter = fields.next().ok_or(FromFenError)?;
|
|
||||||
let full_move_counter: u16 = full_move_counter.parse().map_err(|_| FromFenError)?;
|
|
||||||
builder.move_number(full_move_counter);
|
|
||||||
|
|
||||||
debug_assert_eq!(fields.next(), None);
|
|
||||||
|
|
||||||
Ok(builder.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromFen for Color {
|
|
||||||
type Error = FromFenError;
|
|
||||||
|
|
||||||
fn from_fen_str(string: &str) -> Result<Self, Self::Error> {
|
|
||||||
if string.len() != 1 {
|
|
||||||
return Err(FromFenError);
|
|
||||||
}
|
|
||||||
|
|
||||||
match string.chars().take(1).next().unwrap() {
|
|
||||||
'w' => Ok(Color::White),
|
|
||||||
'b' => Ok(Color::Black),
|
|
||||||
_ => Err(FromFenError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromFen for Piece {
|
|
||||||
type Error = FromFenError;
|
|
||||||
|
|
||||||
fn from_fen_str(string: &str) -> Result<Self, Self::Error> {
|
|
||||||
if string.len() != 1 {
|
|
||||||
return Err(FromFenError);
|
|
||||||
}
|
|
||||||
|
|
||||||
match string.chars().take(1).next().unwrap() {
|
|
||||||
'P' => Ok(piece!(White Pawn)),
|
|
||||||
'N' => Ok(piece!(White Knight)),
|
|
||||||
'B' => Ok(piece!(White Bishop)),
|
|
||||||
'R' => Ok(piece!(White Rook)),
|
|
||||||
'Q' => Ok(piece!(White Queen)),
|
|
||||||
'K' => Ok(piece!(White King)),
|
|
||||||
'p' => Ok(piece!(Black Pawn)),
|
|
||||||
'n' => Ok(piece!(Black Knight)),
|
|
||||||
'b' => Ok(piece!(Black Bishop)),
|
|
||||||
'r' => Ok(piece!(Black Rook)),
|
|
||||||
'q' => Ok(piece!(Black Queen)),
|
|
||||||
'k' => Ok(piece!(Black King)),
|
|
||||||
_ => Err(FromFenError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::test_position;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn starting_position() {
|
|
||||||
let pos = test_position!(starting);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
pos.to_fen(),
|
|
||||||
Ok(String::from(
|
|
||||||
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_starting_fen() {
|
|
||||||
let pos = fen!("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1").unwrap();
|
|
||||||
let expected = Position::starting();
|
|
||||||
assert_eq!(pos, expected, "{pos:#?}\n{expected:#?}");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,5 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
pub mod fen;
|
|
||||||
|
|
||||||
mod check;
|
mod check;
|
||||||
mod display;
|
mod display;
|
||||||
mod move_generator;
|
mod move_generator;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue