diff options
Diffstat (limited to 'src/board/io.rs')
-rw-r--r-- | src/board/io.rs | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/src/board/io.rs b/src/board/io.rs new file mode 100644 index 0000000..cb73c52 --- /dev/null +++ b/src/board/io.rs @@ -0,0 +1,179 @@ +use std::io::{stdin, stdout, Write}; +use rand::{rngs::StdRng,SeedableRng,Rng}; +use crate::{bitboard::Bitboard, attacks::Attacks, moves::Move, square::Square}; + + +use super::{Board, PieceType, ttable::TTABLE_SIZE}; + +const PIECE_CHARS: [&str; 12] = [ + "♟︎", "♞", "♝", "♜", "♛", "♚", + "♙", "♘", "♗", "♖", "♕", "♔", +]; + +/// Input/Output operations with Board +pub trait IO { + fn print(&self) -> (); + + #[allow(non_snake_case)] + fn from_FEN(fen: String) -> Self; + #[allow(non_snake_case)] + fn to_FEN(&self) -> String; + + fn read_move(&self) -> Result<Move, String>; +} + +impl IO for Board { + 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 (piece_type, piece_bitboard) in self.piece_sets.iter().enumerate() { + if (piece_bitboard & position) > 0 { + found = true; + print!("{} ", PIECE_CHARS[piece_type]); + } + } + if !found { + print!(". "); + } + } + println!(); + } + println!(" a b c d e f g h"); + } + + fn from_FEN(fen: String) -> Self { + let mut piece_sets = [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' => piece_sets[PieceType::Pawn as usize] |= position, + 'N' => piece_sets[PieceType::Knight as usize] |= position, + 'B' => piece_sets[PieceType::Bishop as usize] |= position, + 'R' => piece_sets[PieceType::Rook as usize] |= position, + 'Q' => piece_sets[PieceType::Queen as usize] |= position, + 'K' => piece_sets[PieceType::King as usize] |= position, + 'p' => piece_sets[PieceType::PawnBlack as usize] |= position, + 'n' => piece_sets[PieceType::KnightBlack as usize] |= position, + 'b' => piece_sets[PieceType::BishopBlack as usize] |= position, + 'r' => piece_sets[PieceType::RookBlack as usize] |= position, + 'q' => piece_sets[PieceType::QueenBlack as usize] |= position, + 'k' => piece_sets[PieceType::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; + } + } + + let mut rng = StdRng::seed_from_u64(228); + let zobrist_seed = [(); 781].map(|_| rng.gen()); + + let mut board = Self { + piece_sets, + occupancy: 0, + ply: 0, + attacks: Attacks::new(), + castling_rights: [[true; 2]; 2], // TODO: actualy parse from FEN + ep_target: None, // TODO: parse from FEN + hash: 0, + transposition_table: vec![None; TTABLE_SIZE as usize], + zobrist_seed, + }; + board.update_occupancy(); + board.update_zobrist_hash(); + board + } + + #[allow(non_snake_case)] + fn to_FEN(&self) -> String { + todo!() + } + + fn read_move(&self) -> Result<Move, String> { + print!("\nEnter a move: "); + stdout().flush().unwrap(); + let mut s = String::new(); + stdin().read_line(&mut s).unwrap(); + let chars = &mut s.chars(); + + let source = match Square::from_notation(chars) { + Ok(s) => s, + Err(e) => return Err(e), + }; + let target = match Square::from_notation(chars) { + Ok(s) => s, + Err(e) => return Err(e), + }; + + let moves = self.generate_pseudolegal_moves(); + + let mov = match moves.iter().find(|m| m.source == source && m.target == target) { + Some(m) => *m, + None => return Err(String::from("Move is not valid")), + }; + + Ok(mov) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{bitboard::BitboardFns, square::Square, board::Color}; + + #[test] + fn new_from_default_fen() { + let board = Board::new(); + + board.print(); + board.empty().print("Empty squares"); + + assert_eq!(board.piece_sets[PieceType::Pawn as usize].pop_count(), 8); + assert_eq!(board.piece_sets[PieceType::Knight as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::Bishop as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::Rook as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::Queen as usize].pop_count(), 1); + assert_eq!(board.piece_sets[PieceType::King as usize].pop_count(), 1); + + assert_eq!(board.piece_sets[PieceType::PawnBlack as usize].pop_count(), 8); + assert_eq!(board.piece_sets[PieceType::KnightBlack as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::BishopBlack as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::RookBlack as usize].pop_count(), 2); + assert_eq!(board.piece_sets[PieceType::QueenBlack as usize].pop_count(), 1); + assert_eq!(board.piece_sets[PieceType::KingBlack as usize].pop_count(), 1); + + assert_eq!(board.piece_sets[PieceType::King as usize].bitscan(), Square::E1); + assert_eq!(board.piece_sets[PieceType::QueenBlack as usize].bitscan(), Square::D8); + + assert_eq!(board.occupancy.pop_count(), 32); + assert_eq!(board.empty().pop_count(), 32); + assert_eq!(board.color_occupancy(Color::White).pop_count(), 16); + assert_eq!(board.color_occupancy(Color::Black).pop_count(), 16); + } +} |