diff --git a/src/hecht.rs b/src/hecht.rs new file mode 100644 index 0000000..905798d --- /dev/null +++ b/src/hecht.rs @@ -0,0 +1,234 @@ + +use std::collections::HashMap; + +use crate::solver::Solver; +use crate::playground::Playground; +use crate::utils::Locatable; +use crate::utils::LocatableVec; + +pub struct HechtSolver { +} + + +#[derive(Hash, Clone, Copy, Eq, PartialEq, Debug)] +struct Point { + pub x: usize, + pub y: usize, + pub s: usize, +} + +impl Locatable for Point { + fn get_xy(&self) -> (usize, usize) { + (self.x,self.y) + } +} + +#[derive(Debug)] +struct Action { + pub p : Point, + pub value: u16 +} + +#[derive(Clone)] +pub struct SolverPlayground { + arr: [[u16; 9]; 9], + // todos : HashSet, + todos: LocatableVec, + valid: bool, + rec_level: usize, +} + +impl HechtSolver { + pub fn new() -> HechtSolver { + return HechtSolver {}; + } +} + +impl Solver for HechtSolver { + fn solve(&self, pg: &Playground) -> Option { + SolverPlayground::new(&pg) + .solve() + } +} + +impl SolverPlayground { + + pub fn new(pg: &Playground) -> SolverPlayground { + let mut points : LocatableVec = LocatableVec::new(); + for x in 0..9 { + for y in 0..9 { + points.put(Point{x, y, s: y / 3 * 3 + x / 3}); + } + } + + 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); + } + } + + spg + } + + fn to_playground(&self) -> Option { + 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 { + return None + } + pg.set_value(x, y, *bitmap.get(&value).unwrap_or(&0u8)); + } + } + + Some(pg) + } + + pub fn solve(&mut self) -> Option { + 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 { + self.apply(&action); + } + } + + self.to_playground() + } + + fn set_value(&mut self, p : &Point, value : u8) { + let bit_value = 1u16 << (value - 1); + self.set_bit_value(p, bit_value) + } + + fn set_bit_value(&mut self, p : &Point, bit_value : u16) { + if !self.todos.remove(p) { + // Point was not in todo-list -> no further action required + return; + } + + self.arr[p.y][p.x] = bit_value; + + let toprocess : Vec::<_> = 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(); + + for pos in toprocess { + self.remove_value(&pos, bit_value); + } + } + + fn remove_value(&mut self, p : &Point, bit_value: u16) { + if (self.get_value(p) & bit_value) == 0 { + return; + } + self.arr[p.y][p.x] &= !bit_value; + + if let Some(action) = SolverPlayground::create_action(p, self.arr[p.y][p.x]) { + self.apply(&action); + } else if self.arr[p.y][p.x] == 0 { + self.valid = false; + } + } + + fn create_action(p : &Point, bit_value: u16) -> Option { + if bit_value.count_ones() != 1 { + return None; + } + Some(Action{p : p.clone(), value : bit_value}) + } + + fn apply(&mut self, action : &Action) { + self.set_bit_value(&action.p, action.value); + } + + #[inline] + fn get_bitvalue_sequence() -> &'static[u16; 9] { + return &[0b0_0000_0001u16, // 1 + 0b0_0000_0010u16, // 2 + 0b0_0000_0100u16, // 3 + 0b0_0000_1000u16, // 4 + 0b0_0001_0000u16, // 5 + 0b0_0010_0000u16, // 6 + 0b0_0100_0000u16, // 7 + 0b0_1000_0000u16, // 8 + 0b1_0000_0000u16]; // 9 + } + + #[inline] + fn get_bitvalue_mapping() -> HashMap { + let sequence = SolverPlayground::get_bitvalue_sequence(); + let mut result : HashMap = 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 { + // println!("Simple Action"); + self.todos.iter() + .filter_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) + .filter(|&rhs| lhs != rhs) + .map(|&rhs| { + let value = self.get_value(&rhs) & own_value; + let row_value = if lhs.x == rhs.x {value} else {0}; + let col_value = if lhs.y == rhs.y {value} else {0}; + let sec_value = if lhs.s == rhs.s {value} else {0}; + + [row_value, col_value, sec_value] + } ) + .reduce(|a,b| [a[0]|b[0], a[1]|b[1], a[2]|b[2]]) + .iter() + .flatten() + .map(|value| value ^ own_value) + .filter(|value| value.count_ones() == 1) + .find_map(|value| SolverPlayground::create_action(lhs, value)) + } ) + .collect() + } + + fn get_complex_action(&self) -> Option { + // println!("Complex Action"); + let result = self.todos.iter() + .min_by_key(|x| self.get_value(x).count_ones()); + + if let Some(point) = result { + return SolverPlayground::get_bitvalue_sequence().iter() + .filter(|&value| value & self.get_value(point) != 0) + .find_map(|&value| { + let mut spg = self.clone(); + spg.rec_level += 1; + spg.set_bit_value(&point, value); + spg.solve() + }) + } + None + } +} + diff --git a/src/main.rs b/src/main.rs index a9f9876..36531c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ +mod utils; mod playground; mod solver; mod mysolver; +mod hecht; -use mysolver::MySolver; +use hecht::HechtSolver; use playground::Playground; use solver::Solver; @@ -155,7 +157,7 @@ mod tests { fn exec_test(field : &str) { let pg = Playground::from(field); - let solver = MySolver::new(); + let solver = HechtSolver::new(); let result = solver.solve(&pg); assert!(result.is_some()); @@ -169,21 +171,21 @@ mod tests { } 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 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}};"#; let pg = Playground::from(field); pg.print(); - let solver = MySolver::new(); + let solver = HechtSolver::new(); println!(""); @@ -201,8 +203,4 @@ fn main() { } else { println!("Solver was not able to resolve the sodoku!") } - - - - } diff --git a/src/mysolver.rs b/src/mysolver.rs index d718dd6..f6e68fc 100644 --- a/src/mysolver.rs +++ b/src/mysolver.rs @@ -11,7 +11,7 @@ impl MySolver { } impl Solver for MySolver { - fn solve(&self, pg: &Playground) -> Option { + fn solve(&self, _pg: &Playground) -> Option { // FIXME: Implement! None } diff --git a/src/playground.rs b/src/playground.rs index a406638..2f69f49 100644 --- a/src/playground.rs +++ b/src/playground.rs @@ -34,21 +34,25 @@ impl Playground { } pub fn print(&self) { - println!("-------------------------------------"); + println!("┌───────┬───────┬───────┐"); for y in 0..9 { + if y != 0 && y%3 == 0 { + println!("├───────┼───────┼───────┤"); + } print!("|"); for x in 0..9 { - let value = self.get_value(x, y); - if value.is_some() { - print!(" {} |", value.unwrap()); + if let Some(value) = self.get_value(x, y) { + print!(" {}", value); } else { - print!(" |"); + print!(" "); + } + if (x + 1) % 3 == 0 { + print!(" |"); } } println!(); - - println!("-------------------------------------"); } + println!("└───────┴───────┴───────┘"); } pub fn get_value(&self, x: usize, y: usize) -> Option { diff --git a/src/solver.rs b/src/solver.rs index fd077c8..d133a30 100644 --- a/src/solver.rs +++ b/src/solver.rs @@ -4,3 +4,16 @@ use crate::playground::Playground; pub trait Solver { fn solve(&self, _: &Playground) -> Option; } + +enum SodokuComplexity { + Trivial, + Easy, + Average, + Hard, + Difficult, + Impossibru +} + +pub trait Generator{ + fn generate(&self, _complexity:&SodokuComplexity) -> Playground; +} \ No newline at end of file diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..e2de6f4 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,72 @@ + + +pub trait Locatable { + fn get_xy(&self) -> (usize, usize); +} + +pub trait LocatableFactory { + fn create(x:usize, y:usize) -> T; +} + +#[derive(Clone)] +pub struct LocatableVec { + arr: [Option; 81], + len: usize, +} + +pub struct LocatableVecIter<'a, T:Locatable + std::marker::Copy> { + vec : &'a LocatableVec, + idx: usize, +} // (&'a LocatableVec, usize); + + +impl LocatableVec { + + pub fn new() -> LocatableVec { + LocatableVec{arr: [None; 81], len: 0} + } + + pub fn iter(&self) -> LocatableVecIter<'_, T> { + LocatableVecIter{vec: self, idx: 0} + } + + pub fn len(&self) -> usize { + self.len + } + + pub fn put(&mut self, value : T) { + let (x,y) = value.get_xy(); + let idx = y * 9 + x; + if self.arr[idx].is_none() { + self.len += 1; + } + self.arr[idx] = Some(value); + } + + pub fn remove(&mut self, value : &T) -> bool{ + let (x,y) = value.get_xy(); + let idx = y * 9 + x; + if self.arr[idx].is_some() { + self.arr[idx] = None; + self.len -= 1; + return true; + } + false + } + +} + +impl<'a, T:Locatable + std::marker::Copy> Iterator for LocatableVecIter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + for i in self.idx..self.vec.arr.len() { + let pos = &self.vec.arr[i]; + if pos.is_some() { + self.idx = i + 1; + return pos.as_ref(); + } + } + None + } +} \ No newline at end of file