initial commit

This commit is contained in:
Lauchmelder 2022-07-06 21:56:40 +02:00
commit 2153cdb3ab
7 changed files with 379 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target/
.vscode/

45
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,45 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'sudoku-solver'",
"cargo": {
"args": [
"build",
"--bin=sudoku-solver",
"--package=sudoku-solver"
],
"filter": {
"name": "sudoku-solver",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'sudoku-solver'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=sudoku-solver",
"--package=sudoku-solver"
],
"filter": {
"name": "sudoku-solver",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

75
Cargo.lock generated Normal file
View file

@ -0,0 +1,75 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "sudoku-solver"
version = "0.1.0"
dependencies = [
"rand",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "sudoku-solver"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.5"

7
README.md Normal file
View file

@ -0,0 +1,7 @@
# Sudoku Solver
This program can find solutions to Sudoku.
It uses an heuristic search algorithm to optimize a random initial solution.
Also the code is very bad.

59
src/main.rs Normal file
View file

@ -0,0 +1,59 @@
mod sudoku;
use sudoku::{Sudoku, State};
use rand::seq::SliceRandom;
fn main() {
let mut sudoku = Sudoku::new_random(9);
let mut pos = 0;
let mut taboo = vec![Sudoku::new(9); 1024];
let mut counter = 0;
loop
{
match sudoku.solved()
{
State::Solved => { println!("Solved!"); break; },
State::Unsolved(err) =>
{
println!("Iteration {}: {} errors", counter, err);
if counter % 15 == 0
{
println!("{}", sudoku);
}
}
}
let mut neighbours: Vec<(Sudoku, i32)> = vec![];
for neighbour in sudoku.iter_neighbours()
{
if taboo.contains(neighbour) {
continue;
}
let errors = neighbour.errors() as i32;
neighbours.push((neighbour.clone(), errors));
}
neighbours.sort_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap());
for i in 1..neighbours.len()
{
if neighbours[i].1 != neighbours[0].1
{
neighbours = neighbours.chunks(i).next().unwrap().to_vec();
break;
}
}
taboo[pos] = sudoku.clone();
pos = (pos + 1) % taboo.len();
sudoku = neighbours.choose(&mut rand::thread_rng()).unwrap().0.clone();
counter += 1;
}
println!("{}", sudoku)
}

182
src/sudoku.rs Normal file
View file

@ -0,0 +1,182 @@
use std::fmt::Display;
use std::slice::Iter;
pub enum State
{
Solved,
Unsolved(u32)
}
pub struct Sudoku
{
size: usize,
board: Vec<u8>,
pub neighbours: Vec<Sudoku>
}
impl Sudoku
{
pub fn new(size: u8) -> Sudoku
{
// Will result in bad behaviour if size = 0xFF but idc
Sudoku {
size: 9,
board: vec![0; (size * size).into()],
neighbours: vec![]
}
}
pub fn fill_random(&mut self)
{
// Fill board with numbers in order
// TODO: Come up with proper randomized board
let mut crap: Vec<u8> = vec![];
crap.append(&mut vec![1, 2, 3, 1, 2, 3, 1, 2, 3]);
crap.append(&mut vec![4, 5, 6, 4, 5, 6, 4, 5, 6]);
crap.append(&mut vec![7, 8, 9, 7, 8, 9, 7, 8, 9]);
self.board = vec![];
self.board.append(&mut crap.clone());
self.board.append(&mut crap.clone());
self.board.append(&mut crap);
}
pub fn new_random(size: u8) -> Sudoku
{
let mut sudoku = Sudoku::new(size);
sudoku.fill_random();
sudoku
}
pub fn iter_neighbours(&mut self) -> Iter<Sudoku>
{
let indices = [0, 3, 6, 27, 30, 33, 54, 57, 60];
let offsets = [0, 1, 2, 9, 10, 11, 18, 19, 20];
self.neighbours.clear();
for s in indices
{
for i in 0..9
{
for j in i+1..9
{
let mut new_sudoku = self.clone();
new_sudoku.swap(s + offsets[i], s + offsets[j]);
self.neighbours.push(new_sudoku);
}
}
}
self.neighbours.iter()
}
pub fn swap(&mut self, left: usize, right: usize)
{
self.board.swap(left, right);
}
fn check_rows(&self) -> u32
{
let mut errors = 0u32;
for row in self.board.chunks(self.size)
{
for i in 0..self.size
{
for j in i+1..self.size
{
errors += if row[i] == row[j] { 1 } else { 0 };
}
}
}
errors
}
fn check_cols(&self) -> u32
{
let mut errors = 0u32;
for col in 0..self.size
{
for i in 0..self.size
{
for j in i+1..self.size
{
errors += if self.board[col + i * self.size] == self.board[col + j * self.size] { 1 } else { 0 };
}
}
}
errors
}
pub fn errors(&self) -> u32
{
let mut errors = 0u32;
errors += self.check_rows();
errors += self.check_cols();
errors
}
pub fn solved(&self) -> State
{
let errors = self.errors();
match errors
{
0 => State::Solved,
_ => State::Unsolved(errors)
}
}
}
impl Clone for Sudoku
{
fn clone(&self) -> Self
{
Sudoku {
size: self.size,
board: self.board.clone(),
neighbours: vec![]
}
}
}
impl Display for Sudoku
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
for i in 0..self.size
{
for j in 0..self.size
{
write!(f, "{} ", self.board[i * self.size + j])?;
}
writeln!(f, "")?;
}
write!(f, "")
}
}
impl PartialEq for Sudoku
{
fn eq(&self, other: &Self) -> bool
{
for i in 0..self.size
{
if self.board[i] != other.board[i] {
return false;
}
}
true
}
}
impl Eq for Sudoku {}