[explorer] Add several commands to help with debugging
flags : Print flags for the current board position. This prints the castling rights and whether the player can castle (regardless of whether they have the right). make : Finally reimplement the make command. Change the format so it takes a move in the UCI long algebraic style. perft : Run perft to a given depth on the current board position.
This commit is contained in:
parent
6996cbeb15
commit
bf17017694
1 changed files with 92 additions and 9 deletions
|
@ -1,12 +1,14 @@
|
||||||
// Eryn Wells <eryn@erynwells.me>
|
// Eryn Wells <eryn@erynwells.me>
|
||||||
|
|
||||||
use chessfriend_board::ZobristState;
|
use chessfriend_board::castle::CastleEvaluationError;
|
||||||
use chessfriend_board::{Board, fen::FromFenStr};
|
use chessfriend_board::{Board, fen::FromFenStr};
|
||||||
|
use chessfriend_board::{CastleParameters, ZobristState};
|
||||||
use chessfriend_core::random::RandomNumberGenerator;
|
use chessfriend_core::random::RandomNumberGenerator;
|
||||||
use chessfriend_core::{Color, Piece, Shape, Square};
|
use chessfriend_core::{Color, Piece, Shape, Square, Wing};
|
||||||
use chessfriend_moves::GeneratedMove;
|
use chessfriend_moves::algebraic::AlgebraicMoveComponents;
|
||||||
|
use chessfriend_moves::{GeneratedMove, ValidateMove};
|
||||||
use chessfriend_position::{PlacePieceStrategy, Position, fen::ToFenStr};
|
use chessfriend_position::{PlacePieceStrategy, Position, fen::ToFenStr};
|
||||||
use clap::{Arg, Command};
|
use clap::{Arg, Command, value_parser};
|
||||||
use rustyline::DefaultEditor;
|
use rustyline::DefaultEditor;
|
||||||
use rustyline::error::ReadlineError;
|
use rustyline::error::ReadlineError;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -45,6 +47,7 @@ fn command_line() -> Command {
|
||||||
.subcommand_help_heading("COMMANDS")
|
.subcommand_help_heading("COMMANDS")
|
||||||
.help_template(PARSER_TEMPLATE)
|
.help_template(PARSER_TEMPLATE)
|
||||||
.subcommand(Command::new("fen").about("Print the current position as a FEN string"))
|
.subcommand(Command::new("fen").about("Print the current position as a FEN string"))
|
||||||
|
.subcommand(Command::new("flags").about("Print flags for the current position"))
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("load")
|
Command::new("load")
|
||||||
.arg(Arg::new("fen").required(true))
|
.arg(Arg::new("fen").required(true))
|
||||||
|
@ -53,8 +56,7 @@ fn command_line() -> Command {
|
||||||
)
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("make")
|
Command::new("make")
|
||||||
.arg(Arg::new("from").required(true))
|
.arg(Arg::new("move").required(true))
|
||||||
.arg(Arg::new("to").required(true))
|
|
||||||
.alias("m")
|
.alias("m")
|
||||||
.about("Make a move"),
|
.about("Make a move"),
|
||||||
)
|
)
|
||||||
|
@ -81,6 +83,14 @@ fn command_line() -> Command {
|
||||||
.arg(Arg::new("square").required(true))
|
.arg(Arg::new("square").required(true))
|
||||||
.about("Show moves of a piece on a square"),
|
.about("Show moves of a piece on a square"),
|
||||||
)
|
)
|
||||||
|
.subcommand(
|
||||||
|
Command::new("perft")
|
||||||
|
.arg(Arg::new("depth")
|
||||||
|
.required(true)
|
||||||
|
.value_parser(value_parser!(usize))
|
||||||
|
)
|
||||||
|
.about("Run Perft on the current position to the given depth")
|
||||||
|
)
|
||||||
.subcommand(
|
.subcommand(
|
||||||
Command::new("reset")
|
Command::new("reset")
|
||||||
.subcommand(Command::new("clear").about("Reset to a cleared board"))
|
.subcommand(Command::new("clear").about("Reset to a cleared board"))
|
||||||
|
@ -107,6 +117,12 @@ enum CommandHandlingError<'a> {
|
||||||
|
|
||||||
#[error("no piece on {0}")]
|
#[error("no piece on {0}")]
|
||||||
NoPiece(Square),
|
NoPiece(Square),
|
||||||
|
|
||||||
|
#[error("{value:?} is not a valid value for {argument_name:?}")]
|
||||||
|
ValueError {
|
||||||
|
argument_name: &'static str,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fn respond(line: &str, state: &mut State) -> anyhow::Result<CommandResult> {
|
fn respond(line: &str, state: &mut State) -> anyhow::Result<CommandResult> {
|
||||||
|
@ -116,6 +132,7 @@ fn respond(line: &str, state: &mut State) -> anyhow::Result<CommandResult> {
|
||||||
let mut result = CommandResult::default();
|
let mut result = CommandResult::default();
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
|
Some(("flags", matches)) => result = do_flags_command(state, matches),
|
||||||
Some(("load", matches)) => result = do_load_command(state, matches)?,
|
Some(("load", matches)) => result = do_load_command(state, matches)?,
|
||||||
Some(("print", _matches)) => {}
|
Some(("print", _matches)) => {}
|
||||||
Some(("quit", _matches)) => {
|
Some(("quit", _matches)) => {
|
||||||
|
@ -126,9 +143,8 @@ fn respond(line: &str, state: &mut State) -> anyhow::Result<CommandResult> {
|
||||||
println!("{}", state.position.to_fen_str()?);
|
println!("{}", state.position.to_fen_str()?);
|
||||||
result.should_print_position = false;
|
result.should_print_position = false;
|
||||||
}
|
}
|
||||||
Some(("make", _matches)) => {
|
Some(("make", matches)) => result = do_make_command(state, matches)?,
|
||||||
unimplemented!()
|
Some(("perft", matches)) => result = do_perft_command(state, matches)?,
|
||||||
}
|
|
||||||
Some(("place", matches)) => {
|
Some(("place", matches)) => {
|
||||||
let color = matches
|
let color = matches
|
||||||
.get_one::<String>("color")
|
.get_one::<String>("color")
|
||||||
|
@ -175,6 +191,34 @@ fn respond(line: &str, state: &mut State) -> anyhow::Result<CommandResult> {
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_flags_command(state: &mut State, _matches: &clap::ArgMatches) -> CommandResult {
|
||||||
|
let board = &state.position.board;
|
||||||
|
|
||||||
|
println!("Castling:");
|
||||||
|
|
||||||
|
for (color, wing) in [
|
||||||
|
(Color::White, Wing::KingSide),
|
||||||
|
(Color::White, Wing::QueenSide),
|
||||||
|
(Color::Black, Wing::KingSide),
|
||||||
|
(Color::Black, Wing::QueenSide),
|
||||||
|
] {
|
||||||
|
let has_right = board.color_has_castling_right_unwrapped(color, wing);
|
||||||
|
let can_castle = board.color_can_castle(wing, Some(color));
|
||||||
|
|
||||||
|
let can_castle_message = match can_castle {
|
||||||
|
Ok(_) => "ok".to_string(),
|
||||||
|
Err(error) => format!("{error}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(" {color} {wing}: {has_right}, {can_castle_message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandResult {
|
||||||
|
should_continue: true,
|
||||||
|
should_print_position: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn do_load_command(state: &mut State, matches: &clap::ArgMatches) -> anyhow::Result<CommandResult> {
|
fn do_load_command(state: &mut State, matches: &clap::ArgMatches) -> anyhow::Result<CommandResult> {
|
||||||
let fen_string = matches
|
let fen_string = matches
|
||||||
.get_one::<String>("fen")
|
.get_one::<String>("fen")
|
||||||
|
@ -191,6 +235,26 @@ fn do_load_command(state: &mut State, matches: &clap::ArgMatches) -> anyhow::Res
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_make_command(state: &mut State, matches: &clap::ArgMatches) -> anyhow::Result<CommandResult> {
|
||||||
|
let move_string = matches
|
||||||
|
.get_one::<String>("move")
|
||||||
|
.ok_or(CommandHandlingError::MissingArgument("move"))?;
|
||||||
|
|
||||||
|
let algebraic_move: AlgebraicMoveComponents = move_string.parse()?;
|
||||||
|
|
||||||
|
let encoded_move = state
|
||||||
|
.position
|
||||||
|
.move_from_algebraic_components(algebraic_move)
|
||||||
|
.ok_or(CommandHandlingError::ValueError {
|
||||||
|
argument_name: "move",
|
||||||
|
value: move_string.to_string(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
state.position.make_move(encoded_move, ValidateMove::Yes)?;
|
||||||
|
|
||||||
|
Ok(CommandResult::default())
|
||||||
|
}
|
||||||
|
|
||||||
fn do_reset_command(
|
fn do_reset_command(
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
matches: &clap::ArgMatches,
|
matches: &clap::ArgMatches,
|
||||||
|
@ -282,6 +346,25 @@ fn do_movement_command(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn do_perft_command(
|
||||||
|
state: &mut State,
|
||||||
|
matches: &clap::ArgMatches,
|
||||||
|
) -> anyhow::Result<CommandResult> {
|
||||||
|
let depth = *matches
|
||||||
|
.get_one::<usize>("depth")
|
||||||
|
.ok_or(CommandHandlingError::MissingArgument("depth"))?;
|
||||||
|
|
||||||
|
let mut position = state.position.clone();
|
||||||
|
let nodes_count = position.perft(depth);
|
||||||
|
|
||||||
|
println!("nodes {nodes_count}");
|
||||||
|
|
||||||
|
Ok(CommandResult {
|
||||||
|
should_continue: true,
|
||||||
|
should_print_position: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn do_zobrist_command(state: &mut State, _matches: &clap::ArgMatches) -> CommandResult {
|
fn do_zobrist_command(state: &mut State, _matches: &clap::ArgMatches) -> CommandResult {
|
||||||
if let Some(hash) = state.position.zobrist_hash() {
|
if let Some(hash) = state.position.zobrist_hash() {
|
||||||
println!("hash:{hash}");
|
println!("hash:{hash}");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue