initial commit
This commit is contained in:
commit
2153cdb3ab
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
target/
|
||||
.vscode/
|
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal 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
75
Cargo.lock
generated
Normal 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
9
Cargo.toml
Normal 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
7
README.md
Normal 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
59
src/main.rs
Normal 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
182
src/sudoku.rs
Normal 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 {}
|
Loading…
Reference in a new issue