From 8eb180df6714d1c9ba65f5db0b47db8a4efcdbd4 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 28 Jan 2024 09:50:39 -0800 Subject: [PATCH] [explorer] Add fen and make commands Clean up the implementation of the place command. Track state with a State struct that contains a position and a builder. The place command will place a new piece and then regenerate the position. The make command makes a move. The syntax is: make [color:w|b] [shape] [from square] [to square] The fen command prints a FEN string representing the position. --- explorer/src/main.rs | 105 ++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/explorer/src/main.rs b/explorer/src/main.rs index 54b9fdd..0dfe2ad 100644 --- a/explorer/src/main.rs +++ b/explorer/src/main.rs @@ -29,28 +29,31 @@ fn command_line() -> Command { {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") + .subcommand_value_name("CMD") + .subcommand_help_heading("COMMANDS") .help_template(PARSER_TEMPLATE) - .subcommand(Command::new("print").about("Print the board")) + .subcommand(Command::new("fen").about("Print the current position as a FEN string")) + .subcommand( + Command::new("make") + .arg(Arg::new("piece").required(true)) + .arg(Arg::new("from").required(true)) + .arg(Arg::new("to").required(true)) + .about("Make a move"), + ) .subcommand( Command::new("place") + .arg(Arg::new("color").required(true)) .arg(Arg::new("piece").required(true)) - .arg(Arg::new("square").required(true)), + .arg(Arg::new("square").required(true)) + .about("Place a piece on the board"), ) - .subcommand(Command::new("quit").about("Quit the program")) + .subcommand(Command::new("print").about("Print the board")) + .subcommand(Command::new("quit").alias("exit").about("Quit the program")) + .subcommand(Command::new("starting").about("Reset the board to the starting position")) } fn respond(line: &str, state: &mut State) -> Result { @@ -67,20 +70,72 @@ fn respond(line: &str, state: &mut State) -> Result { 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")?; + Some(("fen", _matches)) => { + println!( + "{}", + state + .position + .to_fen() + .map_err(|_| "error: Unable to generate FEN for current position")? + ); - let square_arg = _matches.get_one::("square").unwrap(); - let square = Square::from_algebraic_str(square_arg) + result.should_print_position = false; + } + Some(("make", matches)) => { + let shape = matches + .get_one::("piece") + .ok_or("Missing piece descriptor")?; + let shape = Shape::try_from(shape).map_err(|_| "Invalid piece descriptor")?; + + let from_square = Square::from_algebraic_str( + matches.get_one::("from").ok_or("Missing square")?, + ) + .map_err(|_| "Error: invalid square specifier")?; + + let to_square = Square::from_algebraic_str( + matches.get_one::("to").ok_or("Missing square")?, + ) + .map_err(|_| "Error: invalid square specifier")?; + + let mv = MoveBuilder::new( + Piece::new(state.position.player_to_move(), shape), + from_square, + to_square, + ) + .build(); + + state.position = MakeMoveBuilder::new(&state.position) + .make(&mv) + .map_err(|err| format!("error: Cannot make move: {:?}", err))? + .build(); + state.builder = PositionBuilder::from_position(&state.position); + } + Some(("place", matches)) => { + let color = matches + .get_one::("color") + .ok_or("Missing color descriptor")?; + let color = Color::try_from(color).map_err(|_| "Invalid color descriptor")?; + + let shape = matches + .get_one::("piece") + .ok_or("Missing piece descriptor")?; + let shape = Shape::try_from(shape).map_err(|_| "Invalid piece descriptor")?; + + let square = matches + .get_one::("square") + .ok_or("Missing square")?; + let square = Square::from_algebraic_str(square) .map_err(|_| "Error: invalid square specifier")?; - pos.place_piece(&Piece::new(Color::White, shape), &square) - .map_err(|_| "Error: Unable to place piece")?; + let piece = PlacedPiece::new(Piece::new(color, shape), square); + + state.builder.place_piece(piece); + state.position = state.builder.build(); + } + Some(("starting", _matches)) => { + let starting_position = Position::starting(); + state.builder = PositionBuilder::from_position(&starting_position); + state.position = starting_position; } Some((name, _matches)) => unimplemented!("{name}"), None => unreachable!("Subcommand required"), @@ -107,7 +162,7 @@ fn main() -> Result<(), String> { println!("{} to move.", state.position.player_to_move()); } - let readline = editor.readline("? "); + let readline = editor.readline("\n? "); match readline { Ok(line) => { let line = line.trim();