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, /// 54-character facelet string (UUUU...BBBBBBBBB) describing the cube state #[arg(long)] state: Option, #[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(()) }