use std::io::{stdin, stdout, Write}; use crate::{bitboard::Bitboard, attacks::Attacks, moves::Move, square::Square}; use super::{Board, Piece, ttable::TTABLE_SIZE, zobrist::Zobrist}; 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; } 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[Piece::Pawn as usize] |= position, 'N' => piece_sets[Piece::Knight as usize] |= position, 'B' => piece_sets[Piece::Bishop as usize] |= position, 'R' => piece_sets[Piece::Rook as usize] |= position, 'Q' => piece_sets[Piece::Queen as usize] |= position, 'K' => piece_sets[Piece::King as usize] |= position, 'p' => piece_sets[Piece::PawnBlack as usize] |= position, 'n' => piece_sets[Piece::KnightBlack as usize] |= position, 'b' => piece_sets[Piece::BishopBlack as usize] |= position, 'r' => piece_sets[Piece::RookBlack as usize] |= position, 'q' => piece_sets[Piece::QueenBlack as usize] |= position, 'k' => piece_sets[Piece::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 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::seed(), }; board.update_occupancy(); board.compute_hash(); board } #[allow(non_snake_case)] fn to_FEN(&self) -> String { todo!() } fn read_move(&self) -> Result { 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[Piece::Pawn as usize].pop_count(), 8); assert_eq!(board.piece_sets[Piece::Knight as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::Bishop as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::Rook as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::Queen as usize].pop_count(), 1); assert_eq!(board.piece_sets[Piece::King as usize].pop_count(), 1); assert_eq!(board.piece_sets[Piece::PawnBlack as usize].pop_count(), 8); assert_eq!(board.piece_sets[Piece::KnightBlack as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::BishopBlack as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::RookBlack as usize].pop_count(), 2); assert_eq!(board.piece_sets[Piece::QueenBlack as usize].pop_count(), 1); assert_eq!(board.piece_sets[Piece::KingBlack as usize].pop_count(), 1); assert_eq!(board.piece_sets[Piece::King as usize].bitscan(), Square::E1); assert_eq!(board.piece_sets[Piece::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); } }