Created
July 29, 2026 21:08
-
-
Save jordanorelli/c001fe95a2d5f920497a979f5ffdb4a4 to your computer and use it in GitHub Desktop.
sudoku generator and solver
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #![allow(uncommon_codepoints, confusable_idents, mixed_script_confusables)] | |
| //! ๐งฉ๐ฒ๐ขโจ | |
| use clap::{Parser, Subcommand}; | |
| use rand::rngs::ThreadRng; | |
| use rand::seq::SliceRandom; | |
| use std::io::Read; | |
| use thiserror::Error; | |
| /// ๐ฅ๐งฉ๐ต | |
| #[derive(Debug, Error)] | |
| enum ๐ จ { | |
| /// ๐ฅ๐คโ | |
| #[error("๐ต๐ฅโ๐งฉ")] | |
| ๐ฒ, | |
| /// ๐งฉ๐โ | |
| #[error("๐งฉโ๐๐ข")] | |
| ๐ด, | |
| /// ๐พ๐ฅ | |
| #[error("๐พ๐ฅ {0}")] | |
| ๐ต(#[from] std::io::Error), | |
| } | |
| /// ๐บ๏ธ9๏ธโฃโ๏ธ9๏ธโฃ๐ข | |
| #[derive(Clone)] | |
| struct ๐([[u8; 9]; 9]); | |
| impl ๐ { | |
| /// ๐โฌโฌโฌ๐บ๏ธ | |
| fn ๐() -> Self { | |
| ๐([[0; 9]; 9]) | |
| } | |
| /// ๐ข๐๐ฎโ โ | |
| fn ๐ฃ(&self, ๐ค: usize, ๐ฅ: usize, ๐ฆ: u8) -> bool { | |
| for ๐ญ in 0..9 { | |
| if self.0[๐ค][๐ญ] == ๐ฆ || self.0[๐ญ][๐ฅ] == ๐ฆ { | |
| return false; | |
| } | |
| } | |
| let (๐ฎ, ๐ฏ) = (๐ค / 3 * 3, ๐ฅ / 3 * 3); | |
| for ๐ญ in 0..3 { | |
| for ๐ฐ in 0..3 { | |
| if self.0[๐ฎ + ๐ญ][๐ฏ + ๐ฐ] == ๐ฆ { | |
| return false; | |
| } | |
| } | |
| } | |
| true | |
| } | |
| /// ๐ง๐บ๏ธโ๏ธโ โ | |
| fn ๐(&mut self) -> bool { | |
| for ๐ญ in 0..9 { | |
| for ๐ฎ in 0..9 { | |
| let ๐ฆ = self.0[๐ญ][๐ฎ]; | |
| if ๐ฆ != 0 { | |
| self.0[๐ญ][๐ฎ] = 0; | |
| let ๐ผ = self.๐ฃ(๐ญ, ๐ฎ, ๐ฆ); | |
| self.0[๐ญ][๐ฎ] = ๐ฆ; | |
| if !๐ผ { | |
| return false; | |
| } | |
| } | |
| } | |
| } | |
| true | |
| } | |
| /// ๐ง ๐โ๏ธโ โ | |
| fn ๐(&mut self) -> bool { | |
| for ๐ญ in 0..9 { | |
| for ๐ฎ in 0..9 { | |
| if self.0[๐ญ][๐ฎ] == 0 { | |
| for ๐ฆ in 1..=9 { | |
| if self.๐ฃ(๐ญ, ๐ฎ, ๐ฆ) { | |
| self.0[๐ญ][๐ฎ] = ๐ฆ; | |
| if self.๐() { | |
| return true; | |
| } | |
| self.0[๐ญ][๐ฎ] = 0; | |
| } | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| true | |
| } | |
| /// ๐ข๐ก#๏ธโฃ๐งฎ | |
| fn ๐(&mut self, ๐: &mut u8) { | |
| if *๐ >= 2 { | |
| return; | |
| } | |
| for ๐ญ in 0..9 { | |
| for ๐ฎ in 0..9 { | |
| if self.0[๐ญ][๐ฎ] == 0 { | |
| for ๐ฆ in 1..=9 { | |
| if self.๐ฃ(๐ญ, ๐ฎ, ๐ฆ) { | |
| self.0[๐ญ][๐ฎ] = ๐ฆ; | |
| self.๐(๐); | |
| self.0[๐ญ][๐ฎ] = 0; | |
| } | |
| } | |
| return; | |
| } | |
| } | |
| } | |
| *๐ += 1; | |
| } | |
| /// ๐ฒโ๏ธ๐บ๏ธ๐ | |
| fn ๐น(&mut self, ๐ : &mut ThreadRng) -> bool { | |
| for ๐ญ in 0..9 { | |
| for ๐ฎ in 0..9 { | |
| if self.0[๐ญ][๐ฎ] == 0 { | |
| let mut ๐: [u8; 9] = [1, 2, 3, 4, 5, 6, 7, 8, 9]; | |
| ๐.shuffle(๐ ); | |
| for ๐ฆ in ๐ { | |
| if self.๐ฃ(๐ญ, ๐ฎ, ๐ฆ) { | |
| self.0[๐ญ][๐ฎ] = ๐ฆ; | |
| if self.๐น(๐ ) { | |
| return true; | |
| } | |
| self.0[๐ญ][๐ฎ] = 0; | |
| } | |
| } | |
| return false; | |
| } | |
| } | |
| } | |
| true | |
| } | |
| /// โ๏ธ๐ณ๏ธ๐บ๏ธโ๏ธ๐ก | |
| fn ๐(&mut self, ๐ : &mut ThreadRng, ๐: usize) { | |
| let mut ๐ช: Vec<usize> = (0..81).collect(); | |
| ๐ช.shuffle(๐ ); | |
| let mut ๐พ = 0; | |
| for ๐ข in ๐ช { | |
| if ๐พ >= ๐ { | |
| break; | |
| } | |
| let (๐ญ, ๐ฎ) = (๐ข / 9, ๐ข % 9); | |
| let ๐ฆ = self.0[๐ญ][๐ฎ]; | |
| self.0[๐ญ][๐ฎ] = 0; | |
| let mut ๐ฉ = 0; | |
| self.clone().๐(&mut ๐ฉ); | |
| if ๐ฉ == 1 { | |
| ๐พ += 1; | |
| } else { | |
| self.0[๐ญ][๐ฎ] = ๐ฆ; | |
| } | |
| } | |
| } | |
| /// ๐บ๏ธโก๏ธ๐ผ๏ธ๐ค | |
| fn ๐(&self) -> String { | |
| let mut ๐ = String::new(); | |
| for ๐ญ in 0..9 { | |
| for ๐ฎ in 0..9 { | |
| let ๐ฆ = self.0[๐ญ][๐ฎ]; | |
| if ๐ฆ == 0 { | |
| ๐.push('โฌ'); | |
| } else { | |
| ๐.push_str(&format!("{๐ฆ}\u{fe0f}\u{20e3}")); | |
| } | |
| } | |
| ๐.push('\n'); | |
| } | |
| ๐ | |
| } | |
| /// ๐ฅ๐ผ๏ธโก๏ธ๐บ๏ธ | |
| fn ๐ณ(๐ฟ: &str) -> Result<Self, ๐ จ> { | |
| let ๐ช: Vec<u8> = ๐ฟ | |
| .chars() | |
| .filter_map(|๐ค| match ๐ค { | |
| 'โฌ' => Some(0), | |
| '1'..='9' => Some(๐ค as u8 - b'0'), | |
| _ => None, | |
| }) | |
| .collect(); | |
| if ๐ช.len() != 81 { | |
| return Err(๐ จ::๐ฒ); | |
| } | |
| let mut ๐ = ๐::๐(); | |
| for (๐ข, ๐ฆ) in ๐ช.into_iter().enumerate() { | |
| ๐.0[๐ข / 9][๐ข % 9] = ๐ฆ; | |
| } | |
| Ok(๐) | |
| } | |
| } | |
| /// ๐งฉ๐ ๏ธ๐ช | |
| #[derive(Parser)] | |
| #[command(name = "sudoku")] | |
| struct ๐น { | |
| /// ๐๏ธ๐๏ธ | |
| #[command(subcommand)] | |
| ๐: ๐ฝ, | |
| } | |
| /// ๐๏ธ๐๏ธ๐ | |
| #[derive(Subcommand)] | |
| enum ๐ฝ { | |
| /// ๐ฒ๐งฉโก๏ธ๐ค | |
| #[command(name = "generate")] | |
| ๐ { | |
| /// ๐ณ๏ธ๐ข | |
| #[arg(long = "holes", value_name = "๐ณ๏ธ", default_value_t = 45)] | |
| ๐: usize, | |
| }, | |
| /// ๐ฅ๐งฉ๐ง โก๏ธ๐ค | |
| #[command(name = "solve")] | |
| ๐ด, | |
| } | |
| /// ๐ง ๐ฌ | |
| fn ๐ถ() -> Result<(), ๐ จ> { | |
| let ๐น { ๐ } = ๐น::parse(); | |
| match ๐ { | |
| ๐ฝ::๐ { ๐ } => { | |
| let mut ๐ = rand::rng(); | |
| let mut ๐ = ๐::๐(); | |
| ๐.๐น(&mut ๐ ); | |
| ๐.๐(&mut ๐ , ๐.min(64)); | |
| print!("{}", ๐.๐()); | |
| } | |
| ๐ฝ::๐ด => { | |
| let mut ๐ฟ = String::new(); | |
| std::io::stdin().read_to_string(&mut ๐ฟ)?; | |
| let mut ๐ = ๐::๐ณ(&๐ฟ)?; | |
| if !๐.๐() || !๐.๐() { | |
| return Err(๐ จ::๐ด); | |
| } | |
| print!("{}", ๐.๐()); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| /// ๐๐ | |
| fn main() { | |
| if let Err(๐ ช) = ๐ถ() { | |
| eprintln!("๐ฅ {๐ ช}"); | |
| std::process::exit(1); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment