parent
ebb1d2e8e2
commit
830cc81bc9
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "sudoku-rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
@ -1,3 +1,31 @@
|
||||
# sudoku-solver-rust
|
||||
|
||||
Just a small project to learn rust.
|
||||
|
||||
Update the `mysolver.rs` file with your solver implementation.
|
||||
Some unit tests for corner case testing have been added.
|
||||
|
||||
```rust
|
||||
use crate::solver::Solver;
|
||||
use crate::playground::Playground;
|
||||
|
||||
pub struct MySolver {
|
||||
}
|
||||
|
||||
impl MySolver {
|
||||
pub fn new() -> MySolver {
|
||||
return MySolver {};
|
||||
}
|
||||
}
|
||||
|
||||
impl Solver for MySolver {
|
||||
fn solve(&self, pg: &Playground) -> Option<Playground> {
|
||||
// FIXME: Implement!
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The solver trait hands over self as a read-only reference on purpose.
|
||||
The solver should be stateless, so it can be better compared.
|
@ -0,0 +1,208 @@
|
||||
|
||||
mod playground;
|
||||
mod solver;
|
||||
mod mysolver;
|
||||
|
||||
use mysolver::MySolver;
|
||||
use playground::Playground;
|
||||
use solver::Solver;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// Note this useful idiom: importing names from outer (for mod tests) scope.
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_1() {
|
||||
let field = r#"7 0 6 3 0 8 0 0 9
|
||||
0 0 0 2 6 0 3 0 0
|
||||
0 5 0 0 0 0 0 8 0
|
||||
0 0 0 9 0 1 8 0 2
|
||||
4 0 0 0 0 0 0 0 6
|
||||
9 0 3 8 0 6 0 0 0
|
||||
0 9 0 0 0 0 0 7 0
|
||||
0 0 5 0 2 4 0 0 0
|
||||
6 0 0 1 0 3 2 0 4"#;
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_2() {
|
||||
let field = r#"{0,0,4,1,0,0,3,0,0},
|
||||
{0,7,0,0,0,8,0,0,1},
|
||||
{0,0,0,0,0,0,7,0,9},
|
||||
{8,0,3,0,5,9,0,0,2},
|
||||
{0,0,9,6,0,1,5,0,0},
|
||||
{1,0,0,8,2,0,9,0,4},
|
||||
{6,0,1,0,0,0,0,0,0},
|
||||
{4,0,0,3,0,0,0,9,0},
|
||||
{0,0,8,0,0,4,2,0,0}};"#;
|
||||
|
||||
exec_test(field);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_3() {
|
||||
let field = r#"{0,0,0,0,2,3,4,0,8},
|
||||
{0,0,0,0,7,4,6,9,0},
|
||||
{0,0,0,8,9,0,0,0,7},
|
||||
{0,0,9,3,0,0,7,4,0},
|
||||
{0,7,0,0,4,0,0,3,0},
|
||||
{0,8,3,0,0,7,2,0,0},
|
||||
{5,0,0,0,1,9,0,0,0},
|
||||
{0,9,2,4,3,0,0,0,0},
|
||||
{3,0,6,7,5,0,0,0,0}}"#;
|
||||
|
||||
exec_test(field);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_4() {
|
||||
let field = r#"{0,0,0,0,6,0,0,3,0},
|
||||
{0,9,6,0,0,5,0,0,4},
|
||||
{0,0,0,7,0,1,0,6,0},
|
||||
{0,1,0,0,0,3,0,9,5},
|
||||
{0,4,9,0,1,0,7,2,0},
|
||||
{7,3,0,5,0,0,0,1,0},
|
||||
{0,6,0,9,0,7,0,0,0},
|
||||
{4,0,0,8,0,0,9,5,0},
|
||||
{0,2,0,0,5,0,0,0,0}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_5() {
|
||||
let field = r#"{9,0,6,1,0,7,3,0,4},
|
||||
{0,8,3,0,0,5,0,0,7},
|
||||
{0,0,0,0,0,0,2,0,9},
|
||||
{8,4,5,3,7,1,9,2,6},
|
||||
{3,0,2,5,6,8,0,0,1},
|
||||
{1,0,7,9,2,4,0,3,0},
|
||||
{6,0,9,4,3,2,7,0,8},
|
||||
{0,3,0,0,1,6,5,0,0},
|
||||
{2,1,8,7,5,0,0,0,3}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_6() {
|
||||
let field = r#"{0,8,0,0,0,0,0,0,0},
|
||||
{0,0,0,6,3,0,0,4,1},
|
||||
{0,0,0,5,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,9,0},
|
||||
{0,0,0,0,0,0,0,5,0},
|
||||
{9,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,8,0,2,0,0,0},
|
||||
{0,5,0,0,9,0,0,0,0},
|
||||
{0,0,0,4,5,0,9,0,0}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_7() {
|
||||
let field = r#"{0,0,0,0,0,7,0,2,0},
|
||||
{0,0,4,0,0,0,0,3,0},
|
||||
{0,1,0,0,9,0,0,0,5},
|
||||
{0,0,0,0,0,6,3,0,0},
|
||||
{6,0,0,0,0,0,1,7,0},
|
||||
{0,7,3,4,0,0,0,0,2},
|
||||
{0,0,0,0,6,0,0,9,0},
|
||||
{3,0,7,0,8,0,5,0,0},
|
||||
{0,5,1,0,0,0,0,0,0}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sudoku_8() {
|
||||
let field = r#"{0,0,0,0,0,0,0,1,0},
|
||||
{0,3,0,0,0,4,0,0,0},
|
||||
{0,0,0,0,0,0,3,9,0},
|
||||
{0,4,0,8,0,2,0,0,0},
|
||||
{5,0,0,0,0,0,0,0,0},
|
||||
{0,0,9,0,6,0,2,5,0},
|
||||
{8,0,0,0,3,0,9,0,0},
|
||||
{9,0,0,4,0,0,0,8,0},
|
||||
{1,0,0,6,0,9,0,0,5}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the solver can cope with
|
||||
* an empty board
|
||||
*/
|
||||
#[test]
|
||||
fn test_sudoku_empty() {
|
||||
let field = r#"{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}}"#;
|
||||
|
||||
exec_test(field);
|
||||
}
|
||||
|
||||
|
||||
fn exec_test(field : &str) {
|
||||
let pg = Playground::from(field);
|
||||
let solver = MySolver::new();
|
||||
let result = solver.solve(&pg);
|
||||
|
||||
assert!(result.is_some());
|
||||
let result_pg = result.unwrap();
|
||||
assert!(result_pg.is_valid());
|
||||
assert!(result_pg.is_solved());
|
||||
|
||||
assert!(result_pg.contains(&pg));
|
||||
}
|
||||
|
||||
}
|
||||
fn main() {
|
||||
|
||||
let field = r#"{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0},
|
||||
{0,0,0,0,0,0,0,0,0}}"#;
|
||||
|
||||
let pg = Playground::from(field);
|
||||
|
||||
pg.print();
|
||||
|
||||
let solver = MySolver::new();
|
||||
|
||||
println!("");
|
||||
|
||||
if let Some(result) = solver.solve(&pg) {
|
||||
result.print();
|
||||
|
||||
if !result.contains(&pg) {
|
||||
println!("Solver modified predefined fields!")
|
||||
} if !result.is_valid() {
|
||||
println!("Solver has not correctly solved the sudoku!")
|
||||
} else if !result.is_solved() {
|
||||
println!("Solver was not able to solve the sudoku!")
|
||||
}
|
||||
assert!(result.contains(&pg));
|
||||
} else {
|
||||
println!("Solver was not able to resolve the sodoku!")
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
use crate::solver::Solver;
|
||||
use crate::playground::Playground;
|
||||
|
||||
pub struct MySolver {
|
||||
}
|
||||
|
||||
impl MySolver {
|
||||
pub fn new() -> MySolver {
|
||||
return MySolver {};
|
||||
}
|
||||
}
|
||||
|
||||
impl Solver for MySolver {
|
||||
fn solve(&self, pg: &Playground) -> Option<Playground> {
|
||||
// FIXME: Implement!
|
||||
None
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Playground {
|
||||
arr: [[u8; 9]; 9]
|
||||
}
|
||||
|
||||
impl Playground {
|
||||
|
||||
pub fn init(&mut self, data: String) {
|
||||
let mut idx = 0;
|
||||
for c in data.chars() {
|
||||
idx = match c {
|
||||
'1'..='9' => {
|
||||
let value = c.to_digit(10).unwrap() as u8;
|
||||
self.arr[idx / 9][idx % 9] = value;
|
||||
idx + 1
|
||||
},
|
||||
'0' =>idx + 1,
|
||||
_ => idx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> Playground {
|
||||
return Playground { arr: [[0; 9]; 9] };
|
||||
}
|
||||
|
||||
pub fn from(s: &str) -> Playground {
|
||||
let mut p = Playground::new();
|
||||
p.init(String::from(s));
|
||||
p
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
println!("-------------------------------------");
|
||||
for y in 0..9 {
|
||||
print!("|");
|
||||
for x in 0..9 {
|
||||
let value = self.get_value(x, y);
|
||||
if value.is_some() {
|
||||
print!(" {} |", value.unwrap());
|
||||
} else {
|
||||
print!(" |");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
||||
println!("-------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_value(&self, x: usize, y: usize) -> Option<u8> {
|
||||
match self.arr[y][x] {
|
||||
1..=9 => Some(self.arr[y][x]),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_value(&mut self, x: usize, y: usize, value : u8) {
|
||||
self.arr[y][x] = value;
|
||||
}
|
||||
|
||||
pub fn is_solved(&self) -> bool {
|
||||
return self.arr.iter()
|
||||
.flat_map(|row| row.iter())
|
||||
.all(|value| *value != 0u8);
|
||||
}
|
||||
|
||||
/** Checks if the playground has failures (not neccesary solved) */
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.rows_valid() && self.columns_valid() && self.sections_valid()
|
||||
}
|
||||
|
||||
fn rows_valid(&self) -> bool {
|
||||
let mut rows_set : HashSet<u8> = HashSet::with_capacity(9);
|
||||
|
||||
for x in 0..9 {
|
||||
rows_set.clear();
|
||||
|
||||
for y in 0..9 {
|
||||
if !rows_set.insert(self.arr[y][x]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn columns_valid(&self) -> bool {
|
||||
let mut cols_set : HashSet<u8> = HashSet::with_capacity(9);
|
||||
|
||||
for y in 0..9 {
|
||||
cols_set.clear();
|
||||
|
||||
for x in 0..9 {
|
||||
if !cols_set.insert(self.arr[y][x]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn sections_valid(&self) -> bool {
|
||||
let mut sects_set : HashSet<u8> = HashSet::with_capacity(9);
|
||||
|
||||
for s in 0..9 {
|
||||
sects_set.clear();
|
||||
let ref_point = ((s * 3) % 9, s / 3 * 3);
|
||||
|
||||
for i in 0..9 {
|
||||
let (ref_x,ref_y) = ref_point;
|
||||
if !sects_set.insert(self.arr[ref_y + (i/3)][ref_x + (i%3)]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
pub fn contains(&self, other : &Playground) -> bool {
|
||||
for x in 0..9 {
|
||||
for y in 0..9 {
|
||||
if other.arr[y][x] != 0 && self.arr[y][x] != other.arr[y][x] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
|
||||
use crate::playground::Playground;
|
||||
|
||||
pub trait Solver {
|
||||
fn solve(&self, _: &Playground) -> Option<Playground>;
|
||||
}
|
Loading…
Reference in new issue