|
@@ -445,11 +445,13 @@ impl AnyBoard {
|
|
solutions.reserve(max as usize);
|
|
solutions.reserve(max as usize);
|
|
workset.brute_force(&mut total_solutions, &mut solutions, &groups);
|
|
workset.brute_force(&mut total_solutions, &mut solutions, &groups);
|
|
|
|
|
|
|
|
+ /*
|
|
if solutions.len() > 0 {
|
|
if solutions.len() > 0 {
|
|
println!("*** A Solution:");
|
|
println!("*** A Solution:");
|
|
solutions[0].display();
|
|
solutions[0].display();
|
|
println!("***");
|
|
println!("***");
|
|
}
|
|
}
|
|
|
|
+ */
|
|
(total_solutions, solutions)
|
|
(total_solutions, solutions)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -711,6 +713,7 @@ impl AnySolver {
|
|
/// Validate the board
|
|
/// Validate the board
|
|
/// Reuse reset_possible code, verify the values are possible.
|
|
/// Reuse reset_possible code, verify the values are possible.
|
|
/// - This does not check if the board is completed.
|
|
/// - This does not check if the board is completed.
|
|
|
|
+ /// - It does check that blanks have possible values.
|
|
pub fn validate_board(&mut self) -> bool {
|
|
pub fn validate_board(&mut self) -> bool {
|
|
let mut has_blanks = false;
|
|
let mut has_blanks = false;
|
|
|
|
|
|
@@ -724,9 +727,9 @@ impl AnySolver {
|
|
// I was going to reset_possible, but the board is broken!
|
|
// I was going to reset_possible, but the board is broken!
|
|
// Leave in a broken state.
|
|
// Leave in a broken state.
|
|
|
|
|
|
- // println!("Width: {}, value: {}", self.board.width, value);
|
|
|
|
- println!("Invalid at ({},{}) can't place {}.", x + 1, y + 1, value);
|
|
|
|
- self.board.display();
|
|
|
|
|
|
+ // log (maybe)
|
|
|
|
+ // println!("Invalid at ({},{}) can't place {}.", x + 1, y + 1, value);
|
|
|
|
+ // self.board.display();
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
self.process_move(x, y, value);
|
|
self.process_move(x, y, value);
|
|
@@ -746,8 +749,9 @@ impl AnySolver {
|
|
if value == 0 {
|
|
if value == 0 {
|
|
let count = self.possible.possible[self.possible.pos(x, y)].count_set();
|
|
let count = self.possible.possible[self.possible.pos(x, y)].count_set();
|
|
if count == 0 {
|
|
if count == 0 {
|
|
- println!("Invalid ({},{}) = no values possible.", x + 1, y + 1);
|
|
|
|
- self.board.display();
|
|
|
|
|
|
+ // log (maybe)
|
|
|
|
+ // println!("Invalid ({},{}) = no values possible.", x + 1, y + 1);
|
|
|
|
+ // self.board.display();
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -858,81 +862,92 @@ impl AnySolver {
|
|
true
|
|
true
|
|
}
|
|
}
|
|
|
|
|
|
- // This seems .. broken. Not sure. 4x4 kicked me.
|
|
|
|
- pub fn brute_force_solver(&mut self) -> u16 {
|
|
|
|
- let mut workset = self.clone();
|
|
|
|
- workset.reset_possible();
|
|
|
|
-
|
|
|
|
- let mut total_solutions: u16 = 0;
|
|
|
|
- let mut solutions: Vec<AnyBoard> = Vec::new();
|
|
|
|
- solutions.reserve(1);
|
|
|
|
-
|
|
|
|
- workset.brute_force(&mut total_solutions, &mut solutions);
|
|
|
|
|
|
+ /// Solve by logic alone.
|
|
|
|
+ /// - Returns true when something was added to board.
|
|
|
|
+ /// - Call solve until it returns false.
|
|
|
|
+ /// - It might not be solved (if guessing is required).
|
|
|
|
+ pub fn solve_logic(&mut self) -> bool {
|
|
|
|
+ // Pass 1: Look for singles in the possible sets.
|
|
|
|
+ let mut found_something = false;
|
|
|
|
|
|
- if solutions.len() > 0 {
|
|
|
|
- println!("*** A solution:");
|
|
|
|
- solutions[0].display();
|
|
|
|
- println!("***");
|
|
|
|
|
|
+ for i in 0 .. self.possible.max_index {
|
|
|
|
+ if self.board.board[i] != 0 {
|
|
|
|
+ // Skip, if position already filled.
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if self.possible.possible[i].count_set() == 1 {
|
|
|
|
+ // Get value
|
|
|
|
+ let value = self.possible.possible[i].iter().next().unwrap();
|
|
|
|
+ let pos = self.board.xy(i);
|
|
|
|
+ self.set(pos.0, pos.1, value);
|
|
|
|
+ found_something = true;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- total_solutions
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- fn brute_force(&mut self, total_solutions: &mut u16, solutions: &mut Vec<AnyBoard>) -> bool {
|
|
|
|
- // Verify that possible is setup correctly.
|
|
|
|
- // self.validate_board(); // The better choice here.
|
|
|
|
- // self.reset_possible();
|
|
|
|
|
|
+ let width = self.group.width;
|
|
|
|
+ let grp = self.group.clone();
|
|
|
|
+ let mut values = Bits(0); // HashSet<u8> = HashSet::new();
|
|
|
|
|
|
- for idx in 0..self.board.max_index {
|
|
|
|
- if self.board.board[idx] == 0 {
|
|
|
|
- // Blank found
|
|
|
|
- let (x, y) = self.board.xy(idx);
|
|
|
|
- if DEBUG_OUTPUT {
|
|
|
|
- println!("idx={} ({},{})", idx, x + 1, y + 1);
|
|
|
|
- self.board.display();
|
|
|
|
- self.possible.display();
|
|
|
|
|
|
+ let mut group_process = |this: &mut Self, grp: &[usize]| {
|
|
|
|
+ // Collect all the possible values within the group.
|
|
|
|
+ values.clear();
|
|
|
|
+ for gidx in 0..width {
|
|
|
|
+ // println!("possible: {:?}", this.possible[grp.items[gidx as usize] as usize]);
|
|
|
|
+ for v in this.possible.possible[grp[gidx as usize]].iter() {
|
|
|
|
+ values.set(v, true);
|
|
}
|
|
}
|
|
|
|
+ // values.extend(this.possible[grp.0[gidx as usize] as usize]);
|
|
|
|
+ // println!("now : {:?}", this.possible[grp.items[gidx as usize] as usize]);
|
|
|
|
+ }
|
|
|
|
|
|
- // save possible
|
|
|
|
- let old_possible = self.possible.clone();
|
|
|
|
-
|
|
|
|
- for value in 1..=self.board.width {
|
|
|
|
- if self.possible.get(idx, value as usize) {
|
|
|
|
- // Ok, it could go here.
|
|
|
|
- if DEBUG_OUTPUT {
|
|
|
|
- println!("({},{})={}", x + 1, y + 1, value);
|
|
|
|
- }
|
|
|
|
- self.set(x, y, value);
|
|
|
|
- self.board.display();
|
|
|
|
|
|
+ // println!("values {:?}", values);
|
|
|
|
|
|
- if self.board.complete() {
|
|
|
|
- if *total_solutions < solutions.capacity() as u16 {
|
|
|
|
- solutions.push(self.board.clone());
|
|
|
|
- }
|
|
|
|
- *total_solutions += 1;
|
|
|
|
|
|
+ // Now, check for singles.
|
|
|
|
+ for v in values.iter() {
|
|
|
|
+ let mut count = 0;
|
|
|
|
+ let mut pos = 0;
|
|
|
|
+ for gidx in 0..width{
|
|
|
|
+ if this.possible.possible[grp[gidx as usize] ].get(v as usize) {
|
|
|
|
+ // if this.possible[grp.0[gidx as usize] as usize].contains(&v) {
|
|
|
|
+ count += 1;
|
|
|
|
+ pos = grp[gidx as usize];
|
|
|
|
+ if count > 1 {
|
|
break;
|
|
break;
|
|
- } else {
|
|
|
|
- if self.brute_force(total_solutions, solutions) {
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
}
|
|
}
|
|
- // Restore
|
|
|
|
- self.possible.copy(&old_possible);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- if DEBUG_OUTPUT {
|
|
|
|
- println!("Rewind!");
|
|
|
|
- }
|
|
|
|
- self.set(x, y, 0);
|
|
|
|
- // self.possible.copy(&old_possible);
|
|
|
|
- if DEBUG_OUTPUT {
|
|
|
|
- self.board.display();
|
|
|
|
- self.possible.display();
|
|
|
|
|
|
+ if count == 1 {
|
|
|
|
+ // don't need this, it was v!
|
|
|
|
+ // let value = this.possible[pos as usize].iter().next().cloned().unwrap();
|
|
|
|
+ /*
|
|
|
|
+ if debug {
|
|
|
|
+ println!("Set2 {:?} to {}", xy(pos), v);
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+ let xy = this.board.xy(pos);
|
|
|
|
+ this.set(xy.0, xy.1, v);
|
|
|
|
+ found_something = true;
|
|
}
|
|
}
|
|
- return false;
|
|
|
|
}
|
|
}
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ // Change to 0..WIDTH ... Keep it simple.
|
|
|
|
+ for i in 0..width {
|
|
|
|
+ let mut g = grp.column(i);
|
|
|
|
+ group_process(self, g);
|
|
|
|
+ g = grp.row(i);
|
|
|
|
+ group_process(self, g);
|
|
|
|
+ g = grp.cell(i);
|
|
|
|
+ group_process(self, g);
|
|
}
|
|
}
|
|
- false
|
|
|
|
|
|
+
|
|
|
|
+ if found_something {
|
|
|
|
+ return found_something;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Pair processing.
|
|
|
|
+
|
|
|
|
+ found_something
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -992,7 +1007,7 @@ mod tests {
|
|
"OPC N H F J ",
|
|
"OPC N H F J ",
|
|
" EHJ I MA CK",
|
|
" EHJ I MA CK",
|
|
" FM IPA D",
|
|
" FM IPA D",
|
|
- "FM L H P "
|
|
|
|
|
|
+ "FM L H P "
|
|
]
|
|
]
|
|
);
|
|
);
|
|
// board.display();
|
|
// board.display();
|
|
@@ -1006,7 +1021,7 @@ mod tests {
|
|
// board.display();
|
|
// board.display();
|
|
let strings: Vec<String> = board.to_strings();
|
|
let strings: Vec<String> = board.to_strings();
|
|
assert_eq!(
|
|
assert_eq!(
|
|
- strings,
|
|
|
|
|
|
+ strings,
|
|
vec![
|
|
vec![
|
|
" T DPF Y H R WSX L ",
|
|
" T DPF Y H R WSX L ",
|
|
"L QF J X C G UB D",
|
|
"L QF J X C G UB D",
|
|
@@ -1033,7 +1048,6 @@ mod tests {
|
|
" FR H DQOC K INBW T UY ",
|
|
" FR H DQOC K INBW T UY ",
|
|
"T JL G I P F OC W",
|
|
"T JL G I P F OC W",
|
|
" B KMV Q J H GRI E "
|
|
" B KMV Q J H GRI E "
|
|
-
|
|
|
|
]
|
|
]
|
|
);
|
|
);
|
|
let mut solver = AnySolver::new_from(&board);
|
|
let mut solver = AnySolver::new_from(&board);
|
|
@@ -1053,11 +1067,42 @@ mod tests {
|
|
assert_eq!(9, board.width);
|
|
assert_eq!(9, board.width);
|
|
assert_eq!(81, board.max_index);
|
|
assert_eq!(81, board.max_index);
|
|
|
|
|
|
- board.display();
|
|
|
|
|
|
+ // board.display();
|
|
|
|
+ let strings: Vec<String> = board.to_strings();
|
|
|
|
+ assert_eq!(
|
|
|
|
+ strings,
|
|
|
|
+ vec![
|
|
|
|
+ " 17 83 ",
|
|
|
|
+ " 73 68 ",
|
|
|
|
+ "2 9 4 1",
|
|
|
|
+ " 1 7 ",
|
|
|
|
+ " 2 9 ",
|
|
|
|
+ " 14 86 ",
|
|
|
|
+ " 83 19 ",
|
|
|
|
+ " ",
|
|
|
|
+ "4 2 5 6"
|
|
|
|
+ ]
|
|
|
|
+ );
|
|
|
|
+
|
|
let mut solver = AnySolver::new_from(&board);
|
|
let mut solver = AnySolver::new_from(&board);
|
|
assert!(solver.validate_board());
|
|
assert!(solver.validate_board());
|
|
let solutions = solver.board.brute_force_solver(2);
|
|
let solutions = solver.board.brute_force_solver(2);
|
|
assert!(solutions.0 == 1, "Expected 1 solution, got {}", solutions.0);
|
|
assert!(solutions.0 == 1, "Expected 1 solution, got {}", solutions.0);
|
|
|
|
+ let strings: Vec<String> = solutions.1[0].to_strings();
|
|
|
|
+ assert_eq!(
|
|
|
|
+ strings,
|
|
|
|
+ vec![
|
|
|
|
+ "541768329",
|
|
|
|
+ "973512684",
|
|
|
|
+ "286934751",
|
|
|
|
+ "869157243",
|
|
|
|
+ "325486197",
|
|
|
|
+ "714293865",
|
|
|
|
+ "658341972",
|
|
|
|
+ "197625438",
|
|
|
|
+ "432879516",
|
|
|
|
+ ]
|
|
|
|
+ );
|
|
|
|
|
|
// 4x4 board takes 40 minutes
|
|
// 4x4 board takes 40 minutes
|
|
|
|
|
|
@@ -1093,6 +1138,7 @@ mod tests {
|
|
let mut solver = AnySolver::new(3);
|
|
let mut solver = AnySolver::new(3);
|
|
solver.make();
|
|
solver.make();
|
|
solver.board.display();
|
|
solver.board.display();
|
|
|
|
+ assert!(solver.validate_board());
|
|
}
|
|
}
|
|
|
|
|
|
#[test]
|
|
#[test]
|
|
@@ -1281,6 +1327,15 @@ impl<'a> BoardPossible<'a> {
|
|
}
|
|
}
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ ___ _ _ ____ _
|
|
|
|
+ / _ \| | __| | / ___|___ __| | ___
|
|
|
|
+| | | | |/ _` | | | / _ \ / _` |/ _ \
|
|
|
|
+| |_| | | (_| | | |__| (_) | (_| | __/
|
|
|
|
+ \___/|_|\__,_| \____\___/ \__,_|\___|
|
|
|
|
+
|
|
|
|
+ */
|
|
|
|
+
|
|
#[derive(Debug, Clone, Copy)]
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Sudoku {
|
|
pub struct Sudoku {
|
|
pub board: [u8; MAX_SIZE as usize], // Board
|
|
pub board: [u8; MAX_SIZE as usize], // Board
|