aboutsummaryrefslogtreecommitdiff
path: root/src/board
diff options
context:
space:
mode:
Diffstat (limited to 'src/board')
-rw-r--r--src/board/engine.rs4
-rw-r--r--src/board/io.rs179
-rw-r--r--src/board/mod.rs136
3 files changed, 189 insertions, 130 deletions
diff --git a/src/board/engine.rs b/src/board/engine.rs
index c98348e..5a2b2bf 100644
--- a/src/board/engine.rs
+++ b/src/board/engine.rs
@@ -598,7 +598,7 @@ impl Board {
#[cfg(test)]
mod tests {
use std::time::Duration;
- use crate::{board::{Board, engine::{PerftResult, KING_BONUS}, Color}, square::Square, moves::{Move, MoveKind}};
+ use crate::{board::{Board, engine::{PerftResult, KING_BONUS}, Color, io::IO}, square::Square, moves::{Move, MoveKind}};
use super::VALUE_WIN;
#[test]
@@ -698,7 +698,7 @@ mod tests {
}
mod evaluation {
- use crate::{moves::{Move, MoveKind}, square::Square};
+ use crate::{moves::{Move, MoveKind}, square::Square, board::io::IO};
use super::*;
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);
+ }
+}
diff --git a/src/board/mod.rs b/src/board/mod.rs
index c81b088..f7016d3 100644
--- a/src/board/mod.rs
+++ b/src/board/mod.rs
@@ -1,9 +1,7 @@
-use std::io::{stdin, stdout, Write};
-
-use rand::{rngs::StdRng,SeedableRng,Rng};
-use crate::{bitboard::{Bitboard, BitboardFns}, moves::{Move, MoveKind}, attacks::Attacks, square::Square};
+use crate::{bitboard::{Bitboard, BitboardFns}, moves::{Move, MoveKind}, attacks::Attacks, square::Square, board::io::IO};
use self::ttable::{TranspositionTable, TTABLE_SIZE};
+pub mod io;
mod engine;
mod ttable;
@@ -14,20 +12,19 @@ pub enum CastlingSide {
#[derive(Debug, Clone, PartialEq)]
pub struct Board {
- pub piece_sets: [Bitboard; 12],
-
- pub occupancy: Bitboard,
pub ply: u16,
-
- /// En passsant target square
- pub ep_target: Option<Square>,
-
+ pub piece_sets: [Bitboard; 12],
/// Castling rights indexed by Color and CastlingSide
pub castling_rights: [[bool; 2]; 2],
+ /// En passsant target square
+ pub ep_target: Option<Square>,
+ // Computed values
+ pub occupancy: Bitboard,
/// Zobrist hash of the current position
pub hash: u64,
+ // TODO: remove
transposition_table: TranspositionTable,
zobrist_seed: [u64; 781],
attacks: Attacks,
@@ -70,109 +67,14 @@ impl PieceType {
}
}
-const PIECE_CHARS: [&str; 12] = [
- "♟︎", "♞", "♝", "♜", "♛", "♚",
- "♙", "♘", "♗", "♖", "♕", "♔",
-];
impl Board {
- #[allow(non_snake_case)]
- pub 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
- }
-
pub fn new() -> Self {
let default_fen = String::from("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
Self::from_FEN(default_fen)
}
- pub 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)
- }
-
/// Color to move at this ply
pub fn color(&self) -> Color {
Color::from(self.ply as u8 % 2)
@@ -231,28 +133,6 @@ impl Board {
}
}
- 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 (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 ep_bitboard(&self) -> Bitboard {
let color = self.color();