rubiks-cube-3x3-solver / src/main.rs

raw file 2566 bytes
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(())
}