diff --git a/Cargo.lock b/Cargo.lock index e58f1e8..c6c6f1f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -295,6 +295,17 @@ dependencies = [ "libc", ] +[[package]] +name = "perft" +version = "0.1.0" +dependencies = [ + "anyhow", + "chessfriend_board", + "chessfriend_position", + "clap", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.21" diff --git a/Cargo.toml b/Cargo.toml index 70558c7..3b6ebee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ members = [ "board", "core", "explorer", - "moves", + "moves", "perft", "position", ] resolver = "2" diff --git a/perft/Cargo.toml b/perft/Cargo.toml new file mode 100644 index 0000000..193bb43 --- /dev/null +++ b/perft/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "perft" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.98" +chessfriend_board = { path = "../board" } +chessfriend_position = { path = "../position" } +clap = { version = "4.4.12", features = ["derive"] } +thiserror = "2" diff --git a/perft/src/main.rs b/perft/src/main.rs new file mode 100644 index 0000000..e113579 --- /dev/null +++ b/perft/src/main.rs @@ -0,0 +1,57 @@ +use chessfriend_position::{fen::FromFenStr, GeneratedMove, Position, ValidateMove}; +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(name = "Perft")] +struct Arguments { + depth: usize, + + #[arg(long, short, value_name = "FEN")] + fen: Option, +} + +trait Perft { + fn perft(&mut self, depth: usize) -> u64; +} + +impl Perft for Position { + fn perft(&mut self, depth: usize) -> u64 { + if depth == 0 { + return 1; + } + + let mut nodes_counted: u64 = 0; + + let legal_moves: Vec = self.all_legal_moves(None).collect(); + + for generated_ply in legal_moves { + self.make_move(generated_ply.into(), ValidateMove::No) + .expect("unable to make generated move"); + + nodes_counted += self.perft(depth - 1); + + self.unmake_last_move().expect("unable to unmake last move"); + } + + nodes_counted + } +} + +fn main() -> anyhow::Result<()> { + let args = Arguments::parse(); + let depth = args.depth; + + println!("Searching to depth {depth}"); + + let mut position = if let Some(fen) = args.fen { + Position::from_fen_str(&fen)? + } else { + Position::starting(None) + }; + + let nodes_searched = position.perft(depth); + + println!("Nodes searched: {nodes_searched}"); + + Ok(()) +}