Added CLI parser + reduced memory allocation

master
Hecht 2 years ago
parent d16676c4c4
commit 33684908d7

@ -6,3 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.0.15", features = ["derive"] }
[dev-dependencies]
rstest = "0.15.0"

@ -1,16 +1,15 @@
use std::collections::HashMap;
use crate::solver::Solver;
use crate::playground::Playground;
use crate::utils::Locatable;
use crate::utils::LocatableVec;
use crate::utils::FsVec;
pub struct HechtSolver {
}
#[derive(Hash, Clone, Copy, Eq, PartialEq, Debug)]
#[derive(Hash, Clone, Copy, Eq, PartialEq, Debug, Default)]
struct Point {
pub x: usize,
pub y: usize,
@ -32,8 +31,7 @@ struct Action {
#[derive(Clone)]
pub struct SolverPlayground {
arr: [[u16; 9]; 9],
// todos : HashSet<Point>,
todos: LocatableVec<Point>,
todos: LocatableVec<Point, 81>,
valid: bool,
rec_level: usize,
}
@ -54,15 +52,13 @@ impl Solver for HechtSolver {
impl SolverPlayground {
pub fn new(pg: &Playground) -> SolverPlayground {
let mut points : LocatableVec<Point> = LocatableVec::new();
for x in 0..9 {
for y in 0..9 {
points.put(Point{x, y, s: y / 3 * 3 + x / 3});
}
}
let points : LocatableVec<_, 81> = [0usize..81].into_iter().flatten()
.map(|idx| [idx%9, idx/9])
.map(|p| Point{x: p[0], y:p[1], s: p[1] / 3 * 3 + p[0] / 3})
.collect();
let mut spg = SolverPlayground { arr: [[0x1FFu16; 9]; 9], todos: points.clone(), valid: true, rec_level: 0};
for p in points.iter() {
if let Some(value) = pg.get_value(p.x, p.y) {
spg.set_value(&p, value);
@ -75,15 +71,14 @@ impl SolverPlayground {
fn to_playground(&self) -> Option<Playground> {
let mut pg = Playground::new();
let bitmap = SolverPlayground::get_bitvalue_mapping();
for x in 0..9 {
for y in 0..9 {
let value = self.arr[y][x];
if value == 0 {
if value.count_ones() != 1 {
return None
}
pg.set_value(x, y, *bitmap.get(&value).unwrap_or(&0u8));
pg.set_value(x, y, (value.trailing_zeros() + 1) as u8);
}
}
@ -92,13 +87,10 @@ impl SolverPlayground {
pub fn solve(&mut self) -> Option<Playground> {
while self.valid && self.todos.len() > 0 {
let actions = self.get_simple_action();
if actions.len() == 0 {
return self.get_complex_action();
}
for action in actions {
if let Some(action) = self.get_simple_action() {
self.apply(&action);
} else {
return self.get_complex_action();
}
}
@ -118,19 +110,16 @@ impl SolverPlayground {
self.arr[p.y][p.x] = bit_value;
let toprocess : Vec::<_> = self.todos.iter()
self.todos.iter()
// Filter positions that do not have the right coordinates
.filter(|&pos| pos.x == p.x || pos.y == p.y || pos.s == p.s)
// Filter positions that were known not to hold the value
.filter(|&pos| (self.arr[pos.y][pos.x] & bit_value) != 0)
.cloned()
.collect();
toprocess.iter().next();
.collect::<FsVec::<_,81>>()
.iter()
.for_each(|pos| self.remove_value(&pos, bit_value));
for pos in toprocess {
self.remove_value(&pos, bit_value);
}
}
fn remove_value(&mut self, p : &Point, bit_value: u16) {
@ -170,27 +159,16 @@ impl SolverPlayground {
0b1_0000_0000u16]; // 9
}
#[inline]
fn get_bitvalue_mapping() -> HashMap<u16, u8> {
let sequence = SolverPlayground::get_bitvalue_sequence();
let mut result : HashMap<u16, u8> = HashMap::with_capacity(sequence.len());
for (index, value) in sequence.iter().enumerate() {
result.insert(*value, (index + 1) as u8);
}
result
}
#[inline]
fn get_value(&self, p : &Point) -> u16 {
return self.arr[p.y][p.x];
}
fn get_simple_action(&self) -> Vec<Action> {
fn get_simple_action(&self) -> Option<Action> {
// println!("Simple Action");
self.todos.iter()
.filter_map(|lhs| {
.find_map(|lhs| {
let own_value = self.get_value(&lhs);
self.todos.iter()
.filter(|&rhs| lhs.x == rhs.x || lhs.y == rhs.y || lhs.s == rhs.s)
@ -210,7 +188,6 @@ impl SolverPlayground {
.filter(|value| value.count_ones() == 1)
.find_map(|value| SolverPlayground::create_action(lhs, value))
} )
.collect()
}
fn get_complex_action(&self) -> Option<Playground> {

@ -8,155 +8,129 @@ mod hecht;
use hecht::HechtSolver;
use playground::Playground;
use solver::Solver;
use clap::Parser;
static FIELDS: [[u8;81];9] =
[
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
],
[
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
]
];
#[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);
use rstest::*;
#[rstest]
#[case(0)]
#[case(1)]
#[case(2)]
#[case(3)]
#[case(4)]
#[case(5)]
#[case(6)]
#[case(7)]
#[case(8)]
fn solve_sudoku(#[case] field : usize) {
let pg = Playground::from_array(&FIELDS[field]);
let solver = HechtSolver::new();
let result = solver.solve(&pg);
@ -169,19 +143,21 @@ mod tests {
}
}
fn main() {
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}};"#;
/// Sudoku solver program
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Args {
/// Sudoku index number to use
#[arg(short, long, default_value_t = 1,value_parser = clap::value_parser!(u8).range(0..FIELDS.len() as i64))]
scenario: u8,
}
fn main() {
let args = Args::parse();
let pg = Playground::from(field);
let pg = Playground::from_array(&FIELDS[args.scenario as usize]);
pg.print();

@ -8,28 +8,17 @@ pub struct Playground {
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 {
pub fn from_array(arr: &[u8;81]) -> Playground {
let mut p = Playground::new();
p.init(String::from(s));
for y in 0..9 {
for x in 0..9 {
p.arr[y][x] = arr[y*9+x];
}
}
p
}

@ -4,30 +4,26 @@ pub trait Locatable {
fn get_xy(&self) -> (usize, usize);
}
pub trait LocatableFactory<T:Locatable> {
fn create(x:usize, y:usize) -> T;
}
#[derive(Clone)]
pub struct LocatableVec<T:Locatable + std::marker::Copy> {
arr: [Option<T>; 81],
pub struct LocatableVec<T:Locatable + Copy, const COUNT: usize> {
arr: [Option<T>; COUNT],
len: usize,
}
pub struct LocatableVecIter<'a, T:Locatable + std::marker::Copy> {
vec : &'a LocatableVec<T>,
pub struct LocatableVecIter<'a, T:Locatable + Copy, const COUNT: usize> {
vec : &'a LocatableVec<T, COUNT>,
idx: usize,
} // (&'a LocatableVec<T>, usize);
cnt: usize,
}
impl<T:Locatable + std::marker::Copy> LocatableVec<T> {
impl<T:Locatable + Copy, const COUNT: usize> LocatableVec<T, COUNT> {
pub fn new() -> LocatableVec<T> {
LocatableVec{arr: [None; 81], len: 0}
pub fn new() -> LocatableVec<T, COUNT> {
LocatableVec{arr: [None; COUNT], len: 0}
}
pub fn iter(&self) -> LocatableVecIter<'_, T> {
LocatableVecIter{vec: self, idx: 0}
pub fn iter(&self) -> LocatableVecIter<'_, T, COUNT> {
LocatableVecIter{vec: self, idx: 0, cnt: 0}
}
pub fn len(&self) -> usize {
@ -56,7 +52,7 @@ impl<T:Locatable + std::marker::Copy> LocatableVec<T> {
}
impl<'a, T:Locatable + std::marker::Copy> Iterator for LocatableVecIter<'a, T> {
impl<'a, T:Locatable + Copy, const COUNT: usize> Iterator for LocatableVecIter<'a, T, COUNT> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
@ -64,9 +60,88 @@ impl<'a, T:Locatable + std::marker::Copy> Iterator for LocatableVecIter<'a, T> {
let pos = &self.vec.arr[i];
if pos.is_some() {
self.idx = i + 1;
self.cnt += 1;
return pos.as_ref();
}
}
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.vec.len - self.cnt, Some(self.vec.arr.len() - self.idx))
}
}
impl<'a, L:Locatable + Copy, const COUNT: usize> FromIterator<L> for LocatableVec<L, COUNT> {
fn from_iter<T: IntoIterator<Item = L>>(iter: T) -> Self {
let mut x = LocatableVec{arr: [None; COUNT], len: 0};
for p in iter {
x.put(p);
}
x
}
}
pub struct FsVec<T: Copy + Sized + Default, const COUNT: usize> {
arr: [T; COUNT],
len: usize
}
pub struct FsVecIter<'a, T:Copy + Sized + Default, const COUNT: usize> {
vec : &'a FsVec<T, COUNT>,
idx: usize,
}
impl<T: Sized + Copy + Default, const COUNT: usize> FsVec<T, COUNT> {
pub fn new() -> FsVec<T, COUNT> {
FsVec{arr: [Default::default(); COUNT], len: 0}
}
pub fn len(&self) -> usize {
self.len
}
pub fn append(&mut self, value : T) {
self.arr[self.len] = value;
self.len += 1;
}
pub fn iter(&self) -> FsVecIter<'_, T, COUNT> {
FsVecIter{vec: self, idx: 0}
}
}
impl<'a, T:Sized + Copy + Default, const COUNT: usize> Iterator for FsVecIter<'a, T, COUNT> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.vec.len {
return None
}
let result = Some(&self.vec.arr[self.idx]);
self.idx += 1;
result
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.vec.len() - self.idx, Some(self.vec.len() - self.idx))
}
}
impl<'a, L:Sized + Copy + Default, const COUNT: usize> FromIterator<L> for FsVec<L, COUNT> {
fn from_iter<T: IntoIterator<Item = L>>(iter: T) -> Self {
let mut x = FsVec::new();
for elem in iter {
x.append(elem);
}
x
}
}

Loading…
Cancel
Save