[explorer] A REPL-style command line app to fiddle with chess boards
Build a REPL that enables placing pieces on a chess board. Nothing else so far!
This commit is contained in:
parent
4c9e6a51fc
commit
f340578cf2
3 changed files with 559 additions and 0 deletions
119
explorer/src/main.rs
Normal file
119
explorer/src/main.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use board::piece::{Color, Piece, Shape};
|
||||
use board::position::DiagramFormatter;
|
||||
use board::{Position, Square};
|
||||
use clap::{Arg, Command};
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum ShouldContinue {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
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, pos: &mut Position) -> Result<ShouldContinue, 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())?;
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("print", _matches)) => {}
|
||||
Some(("quit", _matches)) => {
|
||||
return Ok(ShouldContinue::No);
|
||||
}
|
||||
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(ShouldContinue::Yes)
|
||||
}
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
let mut editor = DefaultEditor::new().map_err(|err| format!("Error: {}", err.to_string()))?;
|
||||
|
||||
let mut pos = Position::empty();
|
||||
|
||||
loop {
|
||||
let diagram = DiagramFormatter::new(&pos);
|
||||
println!("{}", diagram);
|
||||
|
||||
let readline = editor.readline("? ");
|
||||
match readline {
|
||||
Ok(line) => {
|
||||
let line = line.trim();
|
||||
if line.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match respond(line, &mut pos) {
|
||||
Ok(should_continue) => {
|
||||
if should_continue == ShouldContinue::No {
|
||||
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(())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue