Skip to content

Instantly share code, notes, and snippets.

@jordanorelli
Created July 29, 2026 21:08
Show Gist options
  • Select an option

  • Save jordanorelli/c001fe95a2d5f920497a979f5ffdb4a4 to your computer and use it in GitHub Desktop.

Select an option

Save jordanorelli/c001fe95a2d5f920497a979f5ffdb4a4 to your computer and use it in GitHub Desktop.
sudoku generator and solver
#![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