From 0167794346336b83aebfe7c794eeb85f2fc34b25 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 8 Jun 2025 17:19:00 -0700 Subject: [PATCH] [perft] A small Perft program --- Cargo.lock | 11 +++++++++ Cargo.toml | 2 +- perft/Cargo.toml | 11 +++++++++ perft/src/main.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 perft/Cargo.toml create mode 100644 perft/src/main.rs 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(()) +}