Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions engine/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ impl ByteKnight {
);
let stdout: io::Stdout = io::stdout();
let mut board = Board::default_board();
let move_gen = chess::move_generation::MoveGenerator::new();
'engine_loop: while let Ok(command) = &self.input_handler.receiver().recv() {
let mut stdout = stdout.lock();

Expand Down Expand Up @@ -191,6 +192,12 @@ impl ByteKnight {
ht.print_for_side(board.side_to_move());
}
}
EngineCommand::Perft(depth) => {
let nodes =
chess::perft::perft(&mut board, &move_gen, *depth as usize, false)
.unwrap();
writeln!(stdout, "info nodes {}", nodes).unwrap();
}
},
}
}
Expand Down
24 changes: 21 additions & 3 deletions engine/src/input_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,33 @@ use uci_parser::UciCommand;
pub(crate) enum EngineCommand {
HashInfo,
History,
Perft(u16),
}

fn split_args(s: &str) -> Vec<String> {
s.split_whitespace()
.map(|part| part.trim().to_string())
.collect()
}

impl FromStr for EngineCommand {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
let args = split_args(s);
if args.is_empty() {
return Err(anyhow::anyhow!("Empty command"));
}
let cmd = args[0].as_str();
let depth = if args.len() > 1 {
args[1].parse::<u16>().unwrap_or(4)
} else {
4
};
match cmd {
"hash" => Ok(EngineCommand::HashInfo),
"history" => Ok(EngineCommand::History),
"perft" => Ok(EngineCommand::Perft(depth)),
_ => Err(anyhow::anyhow!("Invalid engine command")),
}
}
Expand Down Expand Up @@ -90,11 +108,11 @@ impl InputHandler {
break;
}
} else {
eprintln!("Invalid UCI command: {line}");
eprintln!("info error: invalid command: {line}");
}
}
} else {
eprintln!("Error reading from stdin");
eprintln!("info error: failed to read from stdin");
}
}
});
Expand Down
69 changes: 69 additions & 0 deletions src/bin/byte-knight/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
*/

mod bench;
mod perft;

use chess::definitions::DEFAULT_FEN;
use chess::move_generation::MoveGenerator;
use clap::{Parser, Subcommand};
use engine::defs::About;
use engine::engine::ByteKnight;
Expand All @@ -39,6 +42,30 @@ enum Command {
#[arg(short, long)]
epd_file: Option<String>,
},
Perft {
#[arg(short, long, default_value_t = 6)]
depth: usize,
#[arg(
short,
long,
default_value_t = DEFAULT_FEN.to_string()
)]
fen: String,
#[arg(short, long)]
epd_file: Option<String>,
},
SplitPerft {
#[arg(short, long, default_value_t = 6)]
depth: usize,
#[arg(
short,
long,
default_value_t = DEFAULT_FEN.to_string()
)]
fen: String,
#[arg(short, long, default_value_t = false)]
print_moves: bool,
},
}

fn run_uci() {
Expand All @@ -60,6 +87,48 @@ fn main() {
Command::Bench { depth, epd_file } => {
bench::bench(depth, &epd_file);
}
Command::Perft {
depth,
fen,
epd_file,
} => {
let move_gen = MoveGenerator::new();
let board = &mut chess::board::Board::from_fen(&fen).unwrap();
if let Some(epd) = epd_file {
perft::process_epd_file(&epd, &move_gen);
} else {
for i in 1..depth + 1 {
let now = std::time::Instant::now();
let nodes = chess::perft::perft(board, &move_gen, i, false).unwrap();
let elapsed = now.elapsed();
let nps = nodes as f64 / elapsed.as_secs_f64();
println!(
"perft {} = {:>12} {:.2} sec {:>12} nps",
i,
nodes,
elapsed.as_secs_f64(),
nps.round()
);
}
}
}
Command::SplitPerft {
depth,
fen,
print_moves,
} => {
println!("running split perft at depth {}", depth);
let move_gen = MoveGenerator::new();
let board = &mut chess::board::Board::from_fen(&fen).unwrap();
let move_results =
chess::perft::split_perft(board, &move_gen, depth, print_moves).unwrap();
for res in &move_results {
println!("{}: {}", res.mv.to_long_algebraic(), res.nodes);
}
println!();
// print the total nodes
println!("{}", move_results.iter().map(|r| r.nodes).sum::<u64>());
}
},
None => run_uci(),
}
Expand Down
45 changes: 8 additions & 37 deletions src/bin/perft.rs → src/bin/byte-knight/perft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ where
Ok(reader.lines().map(|l| l.unwrap()).collect())
}

fn process_epd_file(path: &str, move_generation: &MoveGenerator) {
/// Process an EPD file and run perft tests on each position.
/// This function assumes the EPD file has fen strings followed by perft information like "D1 20; D2 400; D3 8902".
/// See also the `stardard.epd` file in the data directory of this project.
///
/// # Arguments
/// - `path` - The path to the EPD file.
/// - `move_generation` - The move generator to use for perft calculations.
pub(crate) fn process_epd_file(path: &str, move_generation: &MoveGenerator) {
let mut all_failures = Vec::new();
let lines = read_lines(path).unwrap();
let now = std::time::Instant::now();
Expand Down Expand Up @@ -85,39 +92,3 @@ fn process_epd_file(path: &str, move_generation: &MoveGenerator) {
println!("{fen:<30}: {depth:2} {expected:^10} != {actual:^10}",);
}
}

fn main() {
let args = Args::parse();
let mut board = Board::from_fen(&args.fen).unwrap();
let move_generation = MoveGenerator::new();
if args.epd_file.is_some() {
let path = args.epd_file.as_ref().unwrap();
process_epd_file(path, &move_generation);
} else if args.split_perft {
println!("running split perft at depth {}", args.depth);
let move_results =
perft::split_perft(&mut board, &move_generation, args.depth, args.print_moves).unwrap();
for res in &move_results {
println!("{}: {}", res.mv.to_long_algebraic(), res.nodes);
}
println!();
// print the total nodes
println!("{}", move_results.iter().map(|r| r.nodes).sum::<u64>());
} else {
for i in 1..args.depth + 1 {
let now = std::time::Instant::now();
let nodes = perft::perft(&mut board, &move_generation, i, false).unwrap();
let elapsed = now.elapsed();
let nps = nodes as f64 / elapsed.as_secs_f64();
println!(
"perft {} = {:>12} {:.2} sec {:>12} nps",
i,
nodes,
elapsed.as_secs_f64(),
nps.round()
);
}
};

// println!("{:?}", result);
}