use anyhow::Result;
use clap::{Parser, Subcommand};
use lib::notation;
use rubiks_cube_3x3_solver as lib;
#[derive(Parser)]
#[command(name = "rubiks", version, about = "3x3 Rubik's cube solver")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Solve a scramble given as move notation, or a 54-char facelet state with --state
Solve {
/// Scramble moves (e.g. "R U R' U'")
scramble: Option<String>,
/// 54-character facelet string (UUUU...BBBBBBBBB) describing the cube state
#[arg(long)]
state: Option<String>,
#[arg(long, default_value_t = 20)]
max_length: usize,
#[arg(long, default_value_t = 5.0)]
timeout: f32,
},
/// Generate a random scramble
Scramble {
#[arg(default_value_t = 25)]
length: usize,
},
/// Start a web GUI for entering cube state
Serve {
#[arg(long, default_value_t = 8080)]
port: u16,
},
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Commands::Solve {
scramble,
state,
max_length,
timeout,
} => {
let sol = if let Some(st) = state {
lib::solve_facelet(&st, max_length, timeout)?
} else {
let input = match scramble {
Some(s) => s,
None => {
use std::io::Read;
let mut buf = String::new();
std::io::stdin().read_to_string(&mut buf)?;
buf
}
};
let trimmed = input.trim();
if trimmed.len() == 54 && trimmed.chars().all(|c| "URFDLB".contains(c)) {
lib::solve_facelet(trimmed, max_length, timeout)?
} else {
let moves = notation::parse_moves(trimmed)?;
lib::solve_scramble(&moves, max_length, timeout)?
}
};
println!("{}", lib::notation::format_moves(&sol));
if sol.is_empty() {
println!("(already solved)");
}
}
Commands::Scramble { length } => {
let s = lib::scramble::generate_string(length)?;
println!("{}", s);
}
Commands::Serve { port } => {
let rt = tokio::runtime::Runtime::new()?;
rt.block_on(lib::gui::run(port))?;
}
}
Ok(())
}