144 lines
4.2 KiB
Rust
144 lines
4.2 KiB
Rust
use chessfriend_core::Square;
|
|
use chessfriend_position::{fen::ToFen, MakeMoveBuilder, MoveBuilder, Position, PositionBuilder};
|
|
use clap::{Arg, Command};
|
|
use rustyline::error::ReadlineError;
|
|
use rustyline::DefaultEditor;
|
|
|
|
struct CommandResult {
|
|
should_continue: bool,
|
|
should_print_position: bool,
|
|
}
|
|
|
|
impl Default for CommandResult {
|
|
fn default() -> Self {
|
|
CommandResult {
|
|
should_continue: true,
|
|
should_print_position: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
struct State {
|
|
builder: PositionBuilder,
|
|
position: Position,
|
|
}
|
|
|
|
fn command_line() -> Command {
|
|
// strip out usage
|
|
const PARSER_TEMPLATE: &str = "\
|
|
{all-args}
|
|
";
|
|
|
|
// strip out name/version
|
|
const APPLET_TEMPLATE: &str = "\
|
|
{about-with-newline}\n\
|
|
{usage-heading}\n {usage}\n\
|
|
\n\
|
|
{all-args}{after-help}\
|
|
";
|
|
|
|
Command::new("explorer")
|
|
.multicall(true)
|
|
.arg_required_else_help(true)
|
|
.subcommand_required(true)
|
|
.subcommand_value_name("APPLET")
|
|
.subcommand_help_heading("APPLETS")
|
|
.help_template(PARSER_TEMPLATE)
|
|
.subcommand(Command::new("print").about("Print the board"))
|
|
.subcommand(
|
|
Command::new("place")
|
|
.arg(Arg::new("piece").required(true))
|
|
.arg(Arg::new("square").required(true)),
|
|
)
|
|
.subcommand(Command::new("quit").about("Quit the program"))
|
|
}
|
|
|
|
fn respond(line: &str, state: &mut State) -> Result<CommandResult, String> {
|
|
let args = shlex::split(line).ok_or("error: Invalid quoting")?;
|
|
let matches = command_line()
|
|
.try_get_matches_from(args)
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let mut result = CommandResult::default();
|
|
|
|
match matches.subcommand() {
|
|
Some(("print", _matches)) => {}
|
|
Some(("quit", _matches)) => {
|
|
result.should_continue = false;
|
|
result.should_print_position = false;
|
|
}
|
|
Some(("place", _matches)) => {
|
|
let piece_arg = _matches.get_one::<String>("piece").unwrap();
|
|
let shape = match piece_arg.chars().nth(0) {
|
|
Some(c) => Shape::try_from(c).map_err(|_| ()),
|
|
None => Err(()),
|
|
}
|
|
.map_err(|_| "Error: invalid piece specifier")?;
|
|
|
|
let square_arg = _matches.get_one::<String>("square").unwrap();
|
|
let square = Square::from_algebraic_str(square_arg)
|
|
.map_err(|_| "Error: invalid square specifier")?;
|
|
|
|
pos.place_piece(&Piece::new(Color::White, shape), &square)
|
|
.map_err(|_| "Error: Unable to place piece")?;
|
|
}
|
|
Some((name, _matches)) => unimplemented!("{name}"),
|
|
None => unreachable!("Subcommand required"),
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
|
|
fn main() -> Result<(), String> {
|
|
let mut editor = DefaultEditor::new().map_err(|err| format!("Error: {}", err.to_string()))?;
|
|
|
|
let starting_position = Position::starting();
|
|
let builder = PositionBuilder::from_position(&starting_position);
|
|
let mut state = State {
|
|
builder,
|
|
position: starting_position,
|
|
};
|
|
|
|
let mut should_print_position = true;
|
|
|
|
loop {
|
|
if should_print_position {
|
|
println!("{}", &state.position);
|
|
println!("{} to move.", state.position.player_to_move());
|
|
}
|
|
|
|
let readline = editor.readline("? ");
|
|
match readline {
|
|
Ok(line) => {
|
|
let line = line.trim();
|
|
if line.is_empty() {
|
|
continue;
|
|
}
|
|
|
|
match respond(line, &mut state) {
|
|
Ok(result) => {
|
|
should_print_position = result.should_print_position;
|
|
if !result.should_continue {
|
|
break;
|
|
}
|
|
}
|
|
Err(message) => println!("{}", message),
|
|
}
|
|
}
|
|
Err(ReadlineError::Interrupted) => {
|
|
println!("CTRL-C");
|
|
break;
|
|
}
|
|
Err(ReadlineError::Eof) => {
|
|
println!("CTRL-D");
|
|
break;
|
|
}
|
|
Err(err) => {
|
|
println!("Error: {:?}", err);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|