From d788e73ea5240f72d3ca64a5ce2bad5026643ebd Mon Sep 17 00:00:00 2001 From: eug-vs Date: Sat, 21 Jan 2023 03:33:55 +0300 Subject: feat: implement FEN parsing --- src/board.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/src/board.rs b/src/board.rs index c190c52..991820a 100644 --- a/src/board.rs +++ b/src/board.rs @@ -1,3 +1,117 @@ +use crate::bitboard::Bitboard; + +#[derive(Debug)] +pub struct Board { + pub pieces: [Bitboard; 12], +} + + +enum PieceTypes { + Pawn, + PawnBlack, + Knight, + KnightBlack, + Bishop, + BishopBlack, + Rook, + RookBlack, + Queen, + QueenBlack, + King, + KingBlack, +} + + +const PIECE_CHARS: [&str; 12] = [ + "♟︎", "♙", + "♞", "♘", + "♝", "♗", + "♜", "♖", + "♛", "♕", + "♚", "♔", +]; + + +impl Board { + #[allow(non_snake_case)] + pub fn from_FEN(fen: String) -> Self { + let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + let mut rank = 7; + let mut file = 0i32; + + for character in fen.chars() { + let index = rank * 8 + file; + let position = 1 << index.clamp(0, 63); + + if character.is_numeric() { + let digit = match character.to_digit(10) { + None => todo!("What to do here?"), + Some(digit) => digit, + }; + if digit > 0 && digit <= 8 { + file += digit as i32; + } + } else { + match character { + 'P' => pieces[PieceTypes::Pawn as usize] |= position, + 'N' => pieces[PieceTypes::Knight as usize] |= position, + 'B' => pieces[PieceTypes::Bishop as usize] |= position, + 'R' => pieces[PieceTypes::Rook as usize] |= position, + 'Q' => pieces[PieceTypes::Queen as usize] |= position, + 'K' => pieces[PieceTypes::King as usize] |= position, + 'p' => pieces[PieceTypes::PawnBlack as usize] |= position, + 'n' => pieces[PieceTypes::KnightBlack as usize] |= position, + 'b' => pieces[PieceTypes::BishopBlack as usize] |= position, + 'r' => pieces[PieceTypes::RookBlack as usize] |= position, + 'q' => pieces[PieceTypes::QueenBlack as usize] |= position, + 'k' => pieces[PieceTypes::KingBlack as usize] |= position, + '/' => { + rank -= 1; + file = -1; // So it becomes 0 + }, + ' ' => { break }, // TODO: break for now, parse everything else later + '-' => {}, // TODO + 'w' => {}, // TODO + _ => todo!("Unexpected character!"), + } + file += 1; + } + } + + Self { pieces } + } + + pub fn print(&self) { + println!(); + for rank in (0..8).rev() { + print!("{}|", rank + 1); + for file in 0..8 { + let index = rank * 8 + file; + let position: Bitboard = 1 << index; + let mut found = false; + for (piec_type, piece_bitboard) in self.pieces.iter().enumerate() { + if (piece_bitboard & position) > 0 { + found = true; + print!("{} ", PIECE_CHARS[piec_type]); + } + } + if !found { + print!(". "); + } + } + println!(); + } + println!(" a b c d e f g h"); + } +} + + +enum Color { + White, + Black, +} + /// Aliases to board square indexes #[allow(dead_code)] enum Square { @@ -14,6 +128,7 @@ enum Square { #[cfg(test)] mod tests { use super::*; + use crate::bitboard; #[test] fn test_square_enum() { @@ -21,4 +136,29 @@ mod tests { assert_eq!(Square::F1 as u8, 5); assert_eq!(Square::H8 as u8, 63); } + + #[test] + fn test_from_fen() { + let fen = String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); + let board = Board::from_FEN(fen); + + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Pawn as usize]), 8); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Knight as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Bishop as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Rook as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::Queen as usize]), 1); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::King as usize]), 1); + + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::PawnBlack as usize]), 8); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::KnightBlack as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::BishopBlack as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::RookBlack as usize]), 2); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::QueenBlack as usize]), 1); + assert_eq!(bitboard::pop_count(board.pieces[PieceTypes::KingBlack as usize]), 1); + + assert_eq!(bitboard::bitscan(board.pieces[PieceTypes::King as usize]), Square::E1 as u8); + assert_eq!(bitboard::bitscan(board.pieces[PieceTypes::QueenBlack as usize]), Square::D8 as u8); + + board.print(); + } } -- cgit v1.2.3