From d73630c85e8a72caae22e04b316c154cd62209cf Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Tue, 17 Jun 2025 16:17:46 -0700 Subject: [PATCH] [perft, position] Print moves and nodes counted at first level At the first level of depth, print the move and the number of nodes counted in the tree underneath that node. This behavior imitates Stockfish, and helps with debugging. Clean up the output of the Perft binary, and update the check-positions script to compensate. --- perft/scripts/check-positions | 8 +++--- perft/src/main.rs | 4 +-- position/src/perft.rs | 50 ++++++++++------------------------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/perft/scripts/check-positions b/perft/scripts/check-positions index e5f0626..a7b24ec 100755 --- a/perft/scripts/check-positions +++ b/perft/scripts/check-positions @@ -10,7 +10,7 @@ import json import subprocess -def run_perft(fen, depth, expected_nodes_count): +def run_perft(fen, depth): result = subprocess.run( [ 'cargo', @@ -26,8 +26,8 @@ def run_perft(fen, depth, expected_nodes_count): nodes_count = 0 for line in result.stdout.splitlines(): - if line.startswith('nodes='): - (_, nodes_count) = line.split('=') + if line.startswith('nodes '): + (_, nodes_count) = line.split(' ') nodes_count = int(nodes_count) return nodes_count @@ -61,7 +61,7 @@ def main(argv): print(f'depth={depth}') print(f'expected-nodes={expected_nodes_count}') - nodes_count = run_perft(fen, depth, expected_nodes_count) + nodes_count = run_perft(fen, depth) print(f'nodes={nodes_count}') did_pass = nodes_count == expected_nodes_count diff --git a/perft/src/main.rs b/perft/src/main.rs index 998e28a..70630ae 100644 --- a/perft/src/main.rs +++ b/perft/src/main.rs @@ -14,7 +14,7 @@ fn main() -> anyhow::Result<()> { let args = Arguments::parse(); let depth = args.depth; - println!("depth={depth}"); + println!("depth {depth}"); let mut position = if let Some(fen) = args.fen { Position::from_fen_str(&fen)? @@ -24,7 +24,7 @@ fn main() -> anyhow::Result<()> { let nodes_searched = position.perft(depth); - println!("nodes={nodes_searched}"); + println!("nodes {nodes_searched}"); Ok(()) } diff --git a/position/src/perft.rs b/position/src/perft.rs index 20257fb..57af81a 100644 --- a/position/src/perft.rs +++ b/position/src/perft.rs @@ -8,6 +8,12 @@ pub trait Perft { impl Perft for Position { fn perft(&mut self, depth: usize) -> u64 { + self.perft_recursive(depth, depth) + } +} + +impl Position { + fn perft_recursive(&mut self, depth: usize, max_depth: usize) -> u64 { if depth == 0 { return 1; } @@ -16,50 +22,22 @@ impl Perft for Position { let legal_moves: Vec = self.all_legal_moves(None).collect(); - for generated_ply in legal_moves { - let ply = generated_ply.ply(); + for ply in legal_moves { + let ply = ply.ply(); - let has_seen_position = self + let _has_seen_position = self .make_move(ply, ValidateMove::No) .expect("unable to make generated move"); - // Do not recursive into trees where board positions repeat. - let nodes_counted = if has_seen_position { - 1 - } else { - self.perft(depth - 1) - }; - - total_nodes_counted += nodes_counted; - - self.unmake_last_move().expect("unable to unmake last move"); - } - - total_nodes_counted - } -} - -impl Position { - fn perft_recursive(&mut self, depth: usize) -> u64 { - if depth == 0 { - return 1; - } - - let mut total_nodes_counted: u64 = 0; - - let legal_moves: Vec = self.all_legal_moves(None).collect(); - - for generated_ply in legal_moves { - let ply = generated_ply.ply(); - - self.make_move(ply, ValidateMove::No) - .expect("unable to make generated move"); - - let nodes_counted = self.perft_recursive(depth - 1); + let nodes_counted = self.perft_recursive(depth - 1, depth); total_nodes_counted += nodes_counted; self.unmake_last_move().expect("unable to unmake last move"); + + if depth == max_depth { + println!(" {ply} {nodes_counted}"); + } } total_nodes_counted