From 9a5f9288d3e1e3d7452599201219c1a3d85c6aac Mon Sep 17 00:00:00 2001 From: fabolous005 Date: Sat, 20 Apr 2024 07:20:45 +0200 Subject: [PATCH] add double check resolution + enum iterator --- Cargo.toml | 6 +- src/chess/board.rs | 237 ++++++++++++++++++++++++++++++++++++++++++ src/chess/mod.rs | 3 +- src/chess/moves.rs | 32 ++++++ src/chess/piece.rs | 8 +- src/chess/position.rs | 193 +++++++--------------------------- src/chess/square.rs | 31 +++++- src/config/mod.rs | 2 +- src/main.rs | 6 +- 9 files changed, 352 insertions(+), 166 deletions(-) create mode 100644 src/chess/board.rs diff --git a/Cargo.toml b/Cargo.toml index ef302be..003e309 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,11 @@ custom_pieces = [] [dependencies] toml = { version = "0.8.12" } clap = { version = "4.5.4", features = ["derive"] } -serde = { version = "1.0.197", features = ["derive"] } -serde_derive = { version = "1.0.197" } +serde = { version = "1.0.198", features = ["derive"] } +serde_derive = { version = "1.0.198" } num_cpus = "1.16.0" fern = { version = "0.6.2", features = ["colored"] } log = "0.4.21" humantime = "2.1.0" +strum = "0.26.2" +strum_macros = "0.26.2" diff --git a/src/chess/board.rs b/src/chess/board.rs new file mode 100644 index 0000000..de3ded9 --- /dev/null +++ b/src/chess/board.rs @@ -0,0 +1,237 @@ +use crate::chess::piece::PieceVariant; +use crate::chess::{moves::MoveVariant, piece::Piece}; +use crate::chess::square::Square; +use crate::chess::moves::Move; +use log::{debug, trace}; + +use super::position::Position; + + +#[derive(Debug, Default)] +pub struct Castling { + king_side: bool, + queen_side: bool, +} + +impl Castling { + pub fn king_side(self) -> bool { + self.king_side + } + pub fn queen_side(self) -> bool { + self.queen_side + } +} + +impl Castling { + fn from_fen(fen: String) -> [Self; 2] { + let mut castling = [Castling::default(), Castling::default()]; + for c in fen.chars() { + match c { + 'K' => castling[0].king_side = true, + 'Q' => castling[0].queen_side = true, + 'k' => castling[1].king_side = true, + 'q' => castling[1].queen_side = true, + '-' => (), + _ => panic!("Invalid castling character"), + } + } + castling + } +} + + +// TODO: implement halfmove clock and fullmove number +#[derive(Debug)] +pub struct Board { + rows: [[Option; 8]; 8], + whites_turn: bool, + castling: [Castling; 2], + en_pessant: Option, + position: Position, +} + + +// |-----------------------------------------------| +// | 0;0 | 1;0 | 2;0 | 3;0 | 4;0 | 5;0 | 6;0 | 7;0 | +// |-----------------------------------------------| +// | 0;1 | 1;1 | 2;1 | 3;1 | 4;1 | 5;1 | 6;1 | 7;1 | Black +// |-----------------------------------------------| +// | 0;2 | 1;2 | 2;2 | 3;2 | 4;2 | 5;2 | 6;2 | 7;2 | +// |-----------------------------------------------| +// | 0;3 | 1;3 | 2;3 | 3;3 | 4;3 | 5;3 | 6;3 | 7;3 | +// |-----------------------------------------------| +// | 0;4 | 1;4 | 2;4 | 3;4 | 4;4 | 5;4 | 6;4 | 7;4 | +// |-----------------------------------------------| +// | 0;5 | 1;5 | 2;5 | 3;5 | 4;5 | 5;5 | 6;5 | 7;5 | +// |-----------------------------------------------| +// | 0;6 | 1;6 | 2;6 | 3;6 | 4;6 | 5;6 | 6;6 | 7;6 | White +// |-----------------------------------------------| +// | 0;7 | 1;7 | 2;7 | 3;7 | 4;7 | 5;7 | 6;7 | 7;7 | +// |-----------------------------------------------| +// Chess board repesented in code +// +// NOTE: capture left/right is implemented relative to color +// TODO: make this a doc comment + +impl Default for Board { + fn default() -> Self { + Board { + rows: [ + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + [None, None, None, None, None, None, None, None], + ], + whites_turn: true, + castling: [ + Castling { king_side: false, queen_side: false }, + Castling { king_side: false, queen_side: false }, + ], + en_pessant: None, + position: Position::default(), + } + } +} + + +/// Creating Position +impl Board { + pub fn new() -> Self { + Board::default() + } + + pub fn from_fen(fen: String) -> Self { + trace!("{fen}"); + let mut pos = Board::default(); + let mut parts = fen.split_whitespace(); + + trace!("{:?}", parts); + // parse board + parts.next().unwrap().split('/').enumerate().for_each(|(y, row)| { + let mut x = 0; + row.chars().for_each(|c| { + if c.is_ascii_digit() { + x += c.to_digit(10).unwrap(); + } else { + pos.rows[y][x as usize] = Piece::from_fen(c); + x += 1; + } + }); + }); + + // parse active color + pos.whites_turn = parts.next().unwrap().starts_with('w'); + + // parse castling + pos.castling = Castling::from_fen(parts.next().unwrap().to_string()); + + // parse en passent + pos.en_pessant = Square::en_passent_from_fen(parts.next().unwrap().to_string()); + pos + } + + /* + TODO: Decide if Position should be mutable or not + pub fn set_rows(mut self, rows: [[Option; 8]; 8]) -> Self { + self.rows = rows; + self + } + pub fn set_whites_turn(mut self, whites_turn: bool) -> Self { + self.whites_turn = whites_turn; + self + } + pub fn set_castling(mut self, castling: [Castling; 2]) -> Self { + self.castling = castling; + self + } + pub fn set_en_passent(mut self, en_passent: Option) -> Self { + self.en_pessant = en_passent; + self + } + */ +} + +/// Exposing position fields +impl Board { + pub fn rows(self) -> [[Option; 8]; 8] { + self.rows + } + + pub fn whites_turn(self) -> bool { + self.whites_turn + } + + pub fn castling(self) -> [Castling; 2] { + self.castling + } + + pub fn en_passent(self) -> Option { + self.en_pessant + } + + pub fn position(self) -> Position { + self.position + } + + + + pub fn ref_rows(&mut self) -> &mut [[Option; 8]; 8] { + &mut self.rows + } + + pub fn ref_whites_turn(&mut self) -> &mut bool { + &mut self.whites_turn + } + + pub fn ref_castling(&mut self) -> &mut [Castling; 2] { + &mut self.castling + } + + pub fn ref_en_passent(&mut self) -> &mut Option { + &mut self.en_pessant + } + + pub fn ref_position(&mut self) -> &mut Position { + &mut self.position + } +} + +impl Board { + pub fn king_pos(&self, white: bool) -> Square { + for (y, row) in self.rows.iter().enumerate() { + for (x, piece) in row.iter().enumerate() { + if Some(Piece::new(white, PieceVariant::King)) == *piece { + return Square::new(x as u8, y as u8); + } + } + } + panic!("No King found on board"); + } +} + +/// Use position +impl Board { + pub fn get_moves(&mut self) -> Vec { + if self.ref_position().ref_check().is_some() { + if self.ref_position().ref_check().unwrap() { + let king_pos = self.king_pos(self.whites_turn); + self.ref_position().ref_set_moves(MoveVariant::king_move(king_pos)); + } else { + todo!("take, block or king move"); + } + } + todo!("finish"); + } + + pub fn threatened(&self, square: Square) -> bool { + todo!(); + } + + pub fn threatened_by(&self, square: Square) -> Square { + todo!(); + } +} diff --git a/src/chess/mod.rs b/src/chess/mod.rs index 3b6412c..e545a7a 100644 --- a/src/chess/mod.rs +++ b/src/chess/mod.rs @@ -1,5 +1,6 @@ // TODO: remove this pub later -pub mod position; +pub mod board; pub mod piece; pub mod moves; +mod position; mod square; diff --git a/src/chess/moves.rs b/src/chess/moves.rs index 9d653e2..446bd96 100644 --- a/src/chess/moves.rs +++ b/src/chess/moves.rs @@ -3,6 +3,7 @@ use super::{piece::PieceVariant, square::Square}; #[cfg(feature = "custom_pieces")] use serde_derive::Deserialize; +#[derive(Debug)] pub struct Move { from: Square, to: Square, @@ -42,7 +43,24 @@ impl Change { } } +impl Change { + pub fn x(self) -> i8 { + self.x + } + pub fn y(self) -> i8 { + self.y + } + pub fn ref_x(&self) -> &i8 { + &self.x + } + pub fn ref_y(&self) -> &i8 { + &self.y + } +} + + +#[derive(strum_macros::EnumIter)] pub enum MoveVariant { King, KingCastle, @@ -75,6 +93,20 @@ impl MoveVariant { Change { x: -1, y: -1 }, ] } + pub fn king_move(current: Square) -> Vec { + let mut moves = Vec::new(); + for change in Self::king().iter() { + let new_square = current.add_change(change); + if let Some(new_square) = new_square { + moves.push(Move { + from: current.clone(), + to: new_square, + promotion: None + }); + } + } + moves + } pub fn queen() -> [Change; 8] { [ diff --git a/src/chess/piece.rs b/src/chess/piece.rs index 674c6cc..71790bb 100644 --- a/src/chess/piece.rs +++ b/src/chess/piece.rs @@ -2,7 +2,7 @@ #[cfg(feature = "custom_pieces")] use serde_derive::Deserialize; -#[derive(Debug)] +#[derive(PartialEq, Debug)] pub enum PieceVariant { King, Queen, @@ -12,7 +12,7 @@ pub enum PieceVariant { Pawn } -#[derive(Debug)] +#[derive(PartialEq, Debug)] pub struct Piece { white: bool, variant: PieceVariant @@ -46,7 +46,7 @@ impl Piece { #[cfg(feature = "custom_pieces")] #[derive(Debug, Deserialize)] pub enum Iterability { - Infinite(u8), + Infinite, ForcedNum(u8), Range([u8; 2]) } @@ -54,7 +54,7 @@ pub enum Iterability { #[cfg(feature = "custom_pieces")] impl Default for Iterability { fn default() -> Self { - Self::Infinite(0) + Self::Infinite } } diff --git a/src/chess/position.rs b/src/chess/position.rs index 5c2e0b5..550be54 100644 --- a/src/chess/position.rs +++ b/src/chess/position.rs @@ -1,177 +1,62 @@ -use crate::chess::piece::Piece; -use crate::chess::square::Square; use crate::chess::moves::Move; -use log::{debug, trace}; - #[derive(Debug, Default)] -pub struct Castling { - king_side: bool, - queen_side: bool, -} - -impl Castling { - pub fn king_side(self) -> bool { - self.king_side - } - pub fn queen_side(self) -> bool { - self.queen_side - } -} - -impl Castling { - fn from_fen(fen: String) -> [Self; 2] { - let mut castling = [Castling::default(), Castling::default()]; - for c in fen.chars() { - match c { - 'K' => castling[0].king_side = true, - 'Q' => castling[0].queen_side = true, - 'k' => castling[1].king_side = true, - 'q' => castling[1].queen_side = true, - '-' => (), - _ => panic!("Invalid castling character"), - } - } - castling - } -} - - -// TODO: implement halfmove clock and fullmove number -#[derive(Debug)] pub struct Position { - rows: [[Option; 8]; 8], - whites_turn: bool, - castling: [Castling; 2], - en_pessant: Option + // None => not in check + // Some(false) => single check + // Some(true) => double check + check: Option, + castle_possibilities: [[bool; 2]; 2], + moves: Vec, } - -// |-----------------------------------------------| -// | 0;0 | 1;0 | 2;0 | 3;0 | 4;0 | 5;0 | 6;0 | 7;0 | -// |-----------------------------------------------| -// | 0;1 | 1;1 | 2;1 | 3;1 | 4;1 | 5;1 | 6;1 | 7;1 | Black -// |-----------------------------------------------| -// | 0;2 | 1;2 | 2;2 | 3;2 | 4;2 | 5;2 | 6;2 | 7;2 | -// |-----------------------------------------------| -// | 0;3 | 1;3 | 2;3 | 3;3 | 4;3 | 5;3 | 6;3 | 7;3 | -// |-----------------------------------------------| -// | 0;4 | 1;4 | 2;4 | 3;4 | 4;4 | 5;4 | 6;4 | 7;4 | -// |-----------------------------------------------| -// | 0;5 | 1;5 | 2;5 | 3;5 | 4;5 | 5;5 | 6;5 | 7;5 | -// |-----------------------------------------------| -// | 0;6 | 1;6 | 2;6 | 3;6 | 4;6 | 5;6 | 6;6 | 7;6 | White -// |-----------------------------------------------| -// | 0;7 | 1;7 | 2;7 | 3;7 | 4;7 | 5;7 | 6;7 | 7;7 | -// |-----------------------------------------------| -// Chess board repesented in code -// -// NOTE: capture left/right is implemented relative to color -// TODO: make this a doc comment - -impl Default for Position { - fn default() -> Self { - Position { - rows: [ - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - [None, None, None, None, None, None, None, None], - ], - whites_turn: true, - castling: [ - Castling { king_side: false, queen_side: false }, - Castling { king_side: false, queen_side: false }, - ], - en_pessant: None - } - } -} - - -/// Creating Position impl Position { - pub fn new() -> Self { - Position::default() + pub fn check(self) -> Option { + self.check + } + pub fn castle_possibilities(self) -> [[bool; 2]; 2] { + self.castle_possibilities + } + pub fn moves(self) -> Vec { + self.moves } - pub fn from_fen(fen: String) -> Self { - trace!("{fen}"); - let mut pos = Position::default(); - let mut parts = fen.split_whitespace(); - - trace!("{:?}", parts); - // parse board - parts.next().unwrap().split('/').enumerate().for_each(|(y, row)| { - let mut x = 0; - row.chars().for_each(|c| { - if c.is_ascii_digit() { - x += c.to_digit(10).unwrap(); - } else { - pos.rows[y][x as usize] = Piece::from_fen(c); - x += 1; - } - }); - }); - - // parse active color - pos.whites_turn = parts.next().unwrap().starts_with('w'); - - // parse castling - pos.castling = Castling::from_fen(parts.next().unwrap().to_string()); - - // parse en passent - pos.en_pessant = Square::en_passent_from_fen(parts.next().unwrap().to_string()); - pos + pub fn ref_check(&self) -> &Option { + &self.check } - - /* - TODO: Decide if Position should be mutable or not - pub fn set_rows(mut self, rows: [[Option; 8]; 8]) -> Self { - self.rows = rows; - self + pub fn ref_castle_possibilities(&self) -> &[[bool; 2]; 2] { + &self.castle_possibilities } - pub fn set_whites_turn(mut self, whites_turn: bool) -> Self { - self.whites_turn = whites_turn; - self + pub fn ref_moves(&self) -> &Vec { + &self.moves } - pub fn set_castling(mut self, castling: [Castling; 2]) -> Self { - self.castling = castling; - self - } - pub fn set_en_passent(mut self, en_passent: Option) -> Self { - self.en_pessant = en_passent; - self - } - */ } -/// Exposing position fields impl Position { - pub fn rows(self) -> [[Option; 8]; 8] { - self.rows + pub fn set_check(mut self, check: Option) -> Self { + self.check = check; + self } - - pub fn whites_turn(self) -> bool { - self.whites_turn + pub fn set_castle_possibilities(mut self, castle_possibilities: [[bool; 2]; 2]) -> Self { + self.castle_possibilities = castle_possibilities; + self } - - pub fn castling(self) -> [Castling; 2] { - self.castling - } - - pub fn en_passent(self) -> Option { - self.en_pessant + pub fn set_moves(mut self, moves: Vec) -> Self { + self.moves = moves; + self } } -/// Use position impl Position { - pub fn get_moves(self) -> Vec { - todo!("finish") + pub fn ref_set_check(&mut self, check: Option) { + self.check = check; + } + + pub fn ref_set_castle_possibilities(&mut self, castle_possibilities: [[bool; 2]; 2]) { + self.castle_possibilities = castle_possibilities; + } + + pub fn ref_set_moves(&mut self, moves: Vec) { + self.moves = moves; } } diff --git a/src/chess/square.rs b/src/chess/square.rs index f702320..4e95255 100644 --- a/src/chess/square.rs +++ b/src/chess/square.rs @@ -1,6 +1,8 @@ use log::{debug, trace}; -#[derive(Debug)] +use super::moves::Change; + +#[derive(Debug, Clone)] pub struct Square { x: u8, y: u8, @@ -24,3 +26,30 @@ impl Square { } } } + +impl Square { + pub fn x(self) -> u8 { + self.x + } + pub fn y(self) -> u8 { + self.y + } + pub fn ref_x(&self) -> &u8 { + &self.x + } + pub fn ref_y(&self) -> &u8 { + &self.y + } +} + +impl Square { + pub fn add_change(&self, change: &Change) -> Option { + let x = self.x as i8 + change.ref_x(); + let y = self.y as i8 + change.ref_y(); + if !(0..7).contains(&x) || !(0..7).contains(&y) { + None + } else { + Some(Square::new(x as u8, y as u8)) + } + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 73ab626..88d3ad4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -13,7 +13,7 @@ use clap::Parser; pub fn get_config() -> Option { let args = Args::try_parse().or_else(|err|{ - if err.use_stderr() { + if ! err.use_stderr() { println!("{}", err); return Err(()) } diff --git a/src/main.rs b/src/main.rs index ac8f852..20ebfc0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ )] -use crate::{chess::position::Position, config::get_config}; +use crate::{chess::board::Board, config::get_config}; use log::trace; mod chess; @@ -15,9 +15,9 @@ mod logging; fn main() { - let config = get_config().unwrap(); + let config = get_config().unwrap_or_else(||panic!("")); trace!("{:?}", config); - let board = Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1".to_string()); + let board = Board::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1".to_string()); trace!("{:?}", board); }