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 { 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::("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::("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(()) }