aboutsummaryrefslogtreecommitdiff
path: root/src/board/io.rs
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2023-02-21 12:32:38 +0300
committereug-vs <eugene@eug-vs.xyz>2023-02-21 12:32:38 +0300
commit08e27cdb8aa546833927b678d0a0d3ade1e91ef0 (patch)
treefe149d99038e0ad6dd1dbe0bcd5ab9bf9d908ad2 /src/board/io.rs
parent8cd12afe8d057e9f80c50727dea00b40256bca54 (diff)
downloadchessnost-08e27cdb8aa546833927b678d0a0d3ade1e91ef0.tar.gz
refactor: separate IO module
Diffstat (limited to 'src/board/io.rs')
-rw-r--r--src/board/io.rs179
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);
+ }
+}