use std::prelude::v1::*;
use crate::color::*;
use crate::piece::*;
use crate::square::*;
use crate::common::*;
use crate::bitboard::BitboardExt;
use crate::game::Game;
use crate::piece::PieceChar;
use crate::square::SquareExt;
use crate::positions::Position;
pub trait FEN {
fn from_fen(fen: &str) -> Result<Game, String>;
fn load_fen(&mut self, fen: &str) -> Result<(), String>;
fn to_fen(&self) -> String;
}
impl FEN for Game {
fn from_fen(fen: &str) -> Result<Game, String> {
let mut game = Game::new();
match game.load_fen(fen) {
Ok(()) => Ok(game),
Err(msg) => Err(msg),
}
}
fn load_fen(&mut self, fen: &str) -> Result<(), String> {
self.clear();
self.starting_fen = String::from(fen);
let mut position = Position::new();
let mut fields = fen.split_whitespace();
let mut sq = A8;
if let Some(field) = fields.next() {
for c in field.chars() {
let dir = match c {
'/' => {
2 * DOWN
},
'1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' => {
c.to_digit(10).unwrap() as Shift
},
'P' | 'N' | 'B' | 'R' | 'Q' | 'K' |
'p' | 'n' | 'b' | 'r' | 'q' | 'k' => {
let p = PieceChar::from_char(c);
self.board[sq as usize] = p;
self.bitboards[(p) as usize].set(sq);
self.bitboards[(p & 1) as usize].set(sq); position.hash ^= self.zobrist.pieces[p as usize][sq as usize];
1
},
_ => {
self.load_fen(DEFAULT_FEN)?;
return Err("invalid fen string".into());
}
};
sq = ((sq as i8) + dir) as Square;
}
} else {
self.load_fen(DEFAULT_FEN)?;
return Err("invalid fen string".into());
}
position.side = match fields.next() {
Some("w") => WHITE,
Some("b") => BLACK,
_ => {
self.load_fen(DEFAULT_FEN)?;
return Err("invalid fen string".into());
}
};
if position.side == BLACK {
position.hash ^= self.zobrist.side;
}
if let Some(field) = fields.next() {
for c in field.chars() {
match c {
'K' => {
position.set_castling_right(WHITE, KING);
position.hash ^= self.zobrist.castling_right(WHITE, KING);
}
'Q' => {
position.set_castling_right(WHITE, QUEEN);
position.hash ^= self.zobrist.castling_right(WHITE, QUEEN);
}
'k' => {
position.set_castling_right(BLACK, KING);
position.hash ^= self.zobrist.castling_right(BLACK, KING);
}
'q' => {
position.set_castling_right(BLACK, QUEEN);
position.hash ^= self.zobrist.castling_right(BLACK, QUEEN);
}
_ => break
}
}
}
if let Some(ep) = fields.next() {
if ep != "-" {
position.en_passant = SquareExt::from_coord(ep); position.hash ^= self.zobrist.en_passant[position.en_passant as usize];
}
};
self.positions.push(position);
if let Some(hm) = fields.next() {
if let Ok(n) = hm.parse::<u8>() {
self.positions.set_halfmoves(n);
}
};
if let Some(fm) = fields.next() {
if let Ok(n) = fm.parse::<u8>() {
self.positions.set_fullmoves(n);
}
};
Ok(())
}
fn to_fen(&self) -> String {
let mut fen = String::new();
let mut n = 0;
let mut sq = A8;
loop {
let p = self.board[sq as usize];
if p == EMPTY {
n += 1;
} else {
if n > 0 {
debug_assert!(n < 10);
let c = (b'0' + n) as char;
fen.push(c);
n = 0;
}
fen.push(p.to_char());
}
if sq == H1 {
break;
}
if sq & H1 == H1 { if n > 0 { debug_assert!(n < 10);
let c = (b'0' + n) as char;
fen.push(c);
n = 0;
}
fen.push('/');
sq = ((sq as i8) + 2 * DOWN) as Square; }
sq = ((sq as i8) + RIGHT) as Square; }
fen.push(' ');
if self.side() == WHITE {
fen.push('w');
} else {
fen.push('b');
}
fen.push(' ');
let &pos = self.positions.top();
let mut castles = String::new();
if pos.castling_right(WHITE, KING) {
castles.push('K');
}
if pos.castling_right(WHITE, QUEEN) {
castles.push('Q');
}
if pos.castling_right(BLACK, KING) {
castles.push('k');
}
if pos.castling_right(BLACK, QUEEN) {
castles.push('q');
}
if castles.is_empty() {
castles.push('-');
}
fen.push_str(&castles);
fen.push(' ');
let ep = self.positions.top().en_passant;
if ep < OUT {
fen.push_str(&ep.to_coord());
} else {
fen.push('-');
}
fen.push(' ');
let hm = self.positions.halfmoves();
let fm = self.positions.fullmoves();
fen.push_str(&format!("{} {}", hm, fm));
fen
}
}
#[cfg(test)]
mod tests {
use crate::piece::*;
use crate::square::*;
use crate::common::*;
use crate::fen::FEN;
use crate::game::Game;
#[test]
fn test_from_fen() {
let game = Game::from_fen(DEFAULT_FEN).unwrap();
assert_eq!(game.board[E2 as usize], WHITE_PAWN);
}
#[test]
fn test_to_fen() {
let fens = [
DEFAULT_FEN,
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1",
"rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1",
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1",
"8/8/p1p5/1p5p/1P5p/8/PPP2K1p/4R1rk w - - 4 23"
];
for &fen in fens.iter() {
let game = Game::from_fen(fen).unwrap();
assert_eq!(&game.to_fen(), fen);
}
}
}