|
@@ -7,7 +7,6 @@ use strum::IntoEnumIterator;
|
|
|
extern crate rand_chacha;
|
|
|
use rand::distributions::{Distribution, Uniform};
|
|
|
use rand::seq::SliceRandom;
|
|
|
-
|
|
|
use rand_chacha::rand_core::SeedableRng;
|
|
|
use rand_chacha::ChaCha20Rng;
|
|
|
|
|
@@ -583,6 +582,11 @@ yet another. I think that's how I mentally judge the puzzles.
|
|
|
|
|
|
*/
|
|
|
|
|
|
+pub struct Pairs {
|
|
|
+ pub values: [u8; 2],
|
|
|
+ pub pos: [u8; 2],
|
|
|
+}
|
|
|
+
|
|
|
#[derive(Debug, Clone)]
|
|
|
pub struct AnySolver {
|
|
|
pub board: AnyBoard,
|
|
@@ -638,9 +642,10 @@ impl AnySolver {
|
|
|
/// Process a move
|
|
|
/// - Remove value from rows, columns, and cells.
|
|
|
/// - Clear possibles from (x,y) position, it's filled.
|
|
|
- /// - **THIS PANICS** debug_assert!, panics in test validated_board
|
|
|
- /// TO FIX: This needs to return an error. (someway, somehow)
|
|
|
- pub fn process_move(&mut self, x: u8, y: u8, value: u8) {
|
|
|
+ /// - Added finalize parameter
|
|
|
+ /// - Use it when doing a single move.
|
|
|
+ /// - Don't do it when setting up the board. Call finalize_move then.
|
|
|
+ pub fn process_move(&mut self, x: u8, y: u8, value: u8, finalize: bool) {
|
|
|
debug_assert!(
|
|
|
x <= self.board.width && y <= self.board.width,
|
|
|
"Expected ({}, {}) <= {}",
|
|
@@ -703,8 +708,9 @@ impl AnySolver {
|
|
|
*/
|
|
|
self.cell_poss[cell_index as usize].set(value as usize - 1, false);
|
|
|
|
|
|
- self.finalize_cell(cell_index);
|
|
|
-
|
|
|
+ if finalize {
|
|
|
+ self.finalize_cell(cell_index);
|
|
|
+ }
|
|
|
// Should I do the checks here for the logic possible fix?
|
|
|
// Or, should I make it a different function?
|
|
|
// Put somewhere else, since it has to re-check the entire board!
|
|
@@ -712,16 +718,200 @@ impl AnySolver {
|
|
|
// OR, just check row and columns?
|
|
|
}
|
|
|
|
|
|
+ /// find_pairs: Finds the pairs (and triples, and quads, and ...)
|
|
|
+ /// Must be a vector, because size is self.board.width (dynamic)
|
|
|
+ /// Returns vector[values 0..width], of positions values are seen.
|
|
|
+ /// [0] = "1" = vec![list of positions containing a possible 1]
|
|
|
+ pub fn find_pairs(&mut self, cell_index: u8) -> Vec<GenBits<u32>> {
|
|
|
+ // result[value] = indexes where it is located?
|
|
|
+
|
|
|
+ let initial = GenBits::<u32>(0);
|
|
|
+ let mut result: Vec<GenBits<u32>> = vec![initial; self.board.width as usize];
|
|
|
+
|
|
|
+ // Find starting row and column for given cell.
|
|
|
+
|
|
|
+ // let (col, row) = self.group.cell_start(cell_index);
|
|
|
+ // let size = self.board.size;
|
|
|
+ /*
|
|
|
+ let size = self.board.size;
|
|
|
+ let col = (cell_index % size) * size;
|
|
|
+ let row = (cell_index / size) * size;
|
|
|
+ */
|
|
|
+
|
|
|
+ // println!("Cell {} Starts at {},{}", cell_index, col, row);
|
|
|
+ let cellgroup = self.group.group(Groups::Cell, cell_index);
|
|
|
+
|
|
|
+ for value in 0..self.board.width {
|
|
|
+ // Locate every position with a 1
|
|
|
+ for pos in 0..self.board.width {
|
|
|
+ let cellindex = cellgroup[pos as usize];
|
|
|
+ /*
|
|
|
+ let x = col + (pos % size);
|
|
|
+ let y = row + (pos / size);
|
|
|
+ */
|
|
|
+ // println!("Pos {} ({},{}) Value {}", pos, x, y, value);
|
|
|
+
|
|
|
+ if self
|
|
|
+ .possible
|
|
|
+ .get(cellindex, value as usize + 1)
|
|
|
+ // .get(self.possible.pos(x, y), value as usize + 1)
|
|
|
+ {
|
|
|
+ // println!("Found {} ({},{}) = {}", cell_index, x, y, value);
|
|
|
+ result[value as usize].set(pos as usize, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Ok, this is good, but not useful. ;)
|
|
|
+
|
|
|
+ let zero_bits = GenBits::<u32>(0);
|
|
|
+ let width = self.board.width;
|
|
|
+ let mut possibles_updated : bool = false;
|
|
|
+
|
|
|
+ 'outer: for idx in 0..width-1 {
|
|
|
+ // pos_found contains the positions.
|
|
|
+ let pos_found = result[idx as usize];
|
|
|
+ if pos_found == zero_bits {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if idx > 0 {
|
|
|
+ // Check previous for matches - already checked...
|
|
|
+ for check in 0..idx {
|
|
|
+ if pos_found == result[check as usize] {
|
|
|
+ continue 'outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let count = pos_found.count_set();
|
|
|
+ if count == 1 {
|
|
|
+ // Single possible item here ... skip this for now.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut matches = 1;
|
|
|
+ // Pos contains the numbers in the cell.
|
|
|
+ let mut pos = Vec::<u8>::new();
|
|
|
+ pos.push(idx);
|
|
|
+
|
|
|
+ for look in idx+1..width {
|
|
|
+ if result[look as usize] == pos_found {
|
|
|
+ matches += 1;
|
|
|
+ pos.push(look);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if matches == count {
|
|
|
+ // Ok! We found a pair (or triple, or quad, or ...)
|
|
|
+ // Build new possible
|
|
|
+ let mut new_possible = zero_bits;
|
|
|
+ for p in &pos {
|
|
|
+ new_possible.set(*p as usize, true);
|
|
|
+ }
|
|
|
+ for p in pos_found.iter() {
|
|
|
+ if self.possible.possible[cellgroup[p as usize]] != new_possible {
|
|
|
+ println!("Update pos {} with {:?}", p, new_possible);
|
|
|
+ println!("Index is {} => {:?}", cellgroup[p as usize], self.group.xy(cellgroup[p as usize]));
|
|
|
+ possibles_updated = true;
|
|
|
+ self.possible.possible[cellgroup[p as usize]] = new_possible;
|
|
|
+ println!("Updated!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ result
|
|
|
+ }
|
|
|
+
|
|
|
/// Finalize move on the board.
|
|
|
/// - This uses the cell index last modified, so we have fewer
|
|
|
/// cells to check here. (2 * size) cells.
|
|
|
- pub fn finalize_cell(&mut self, index:u8) {
|
|
|
- let size = self.board.size;
|
|
|
+ pub fn finalize_cell(&mut self, index: u8) {
|
|
|
+ let size = self.board.width;
|
|
|
+ let mut update: bool = false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ let values = self.find_pairs(index);
|
|
|
+ // ok! Scan, and find pairs, triples, etc.
|
|
|
+ println!("Finalize Cell {}: {:?}", index, values);
|
|
|
+
|
|
|
+ 'outer: for idx in 0..size-1 {
|
|
|
+ // Positions found
|
|
|
+ let pos_found = values[idx as usize];
|
|
|
+ if pos_found == (GenBits::<u32>(0)) {
|
|
|
+ println!("Idx {}: (empty)", idx);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ if idx > 0 {
|
|
|
+ // check if previous done
|
|
|
+ for check in 0..idx {
|
|
|
+ if pos_found == values[check as usize] {
|
|
|
+ println!("Previously done {} prev index {} {:?}", idx, check, pos_found);
|
|
|
+ continue 'outer;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ /*
|
|
|
+ if idx == size-2 {
|
|
|
+ break 'outer;
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ // Check to see:
|
|
|
+ // - How many items are in it.
|
|
|
+ // - Can we find that many matches.
|
|
|
+
|
|
|
+ /*
|
|
|
+ let count = pos_found.count_set();
|
|
|
+
|
|
|
+ if count == 1 {
|
|
|
+ // Or - should we process this single possible value here?
|
|
|
+ println!("Idx {}: has one item.", idx);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut matches = 1;
|
|
|
+ let mut pos = Vec::<u8>::new();
|
|
|
+ pos.push(idx);
|
|
|
+
|
|
|
+ println!("Starting at {}, we're looking for {:?} [{} times]", idx, pos_found, count);
|
|
|
+
|
|
|
+ for look in idx+1..size {
|
|
|
+ if values[look as usize] == pos_found {
|
|
|
+ matches += 1;
|
|
|
+ pos.push(look);
|
|
|
+ println!("Match found at {} ({} total)", look, matches);
|
|
|
+ } else {
|
|
|
+ println!("{:?} did not match {:?} at {}", pos_found, values[look as usize], look);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ println!("Final {} matches found, looking for {}", matches, count);
|
|
|
+
|
|
|
+ if matches == count {
|
|
|
+ // Pair (or something) found!
|
|
|
+ println!("Pairs: {:?} [value {}]", values, idx);
|
|
|
+ println!("FOUND: Index {} Count {} Value {:?} {:?}", index, count, pos_found, pos);
|
|
|
+ // Ok! In this position, remove any other possible!
|
|
|
+ // What position am I talking about here?
|
|
|
+ // let index_pos = self.possible.which_cell_index(index, )
|
|
|
+ } else {
|
|
|
+ println!("missed: Index {} Count {} Value {:?} {:?}", index, count, pos_found, pos);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ */
|
|
|
+
|
|
|
+ // println!("Pairs: {:?}", self.find_pairs(index));
|
|
|
|
|
|
// Check columns
|
|
|
for c in 0..size {
|
|
|
// Calculate the row and column for the given cell.
|
|
|
- let _col = (index % size)*size + c;
|
|
|
+ let _col = (index % size) * size + c;
|
|
|
// println!("Index {} COL {}", index, col);
|
|
|
}
|
|
|
|
|
@@ -731,22 +921,19 @@ impl AnySolver {
|
|
|
let _row = (index / size) * size + r;
|
|
|
// println!("Index {} ROW {}", index, row);
|
|
|
}
|
|
|
-
|
|
|
+ println!("finalize_cell ends...");
|
|
|
}
|
|
|
|
|
|
/// Finalize move(s) on the board.
|
|
|
/// - This checks the board for logic fixes. (when column is full/has 1+pair/triple)
|
|
|
pub fn finalize_move(&mut self) {
|
|
|
- for idx in 0..3 {
|
|
|
- let mut _ix: u8 = idx * 3;
|
|
|
- let mut _iy: u8 = idx * 3;
|
|
|
-
|
|
|
- println!("Index {} x {} y {}", idx, _ix, _iy);
|
|
|
- // Check cell rows
|
|
|
-
|
|
|
- // Check cell columns
|
|
|
+ // Process the cells diagonally.
|
|
|
+ println!("TODO");
|
|
|
+ /*
|
|
|
+ for idx in 0..self.board.size {
|
|
|
+ self.finalize_cell(idx + idx * self.board.size);
|
|
|
}
|
|
|
- assert!(false, "Boing!");
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
/// Validate the board
|
|
@@ -779,13 +966,14 @@ impl AnySolver {
|
|
|
// self.board.display();
|
|
|
return false;
|
|
|
}
|
|
|
- self.process_move(x, y, value);
|
|
|
+ self.process_move(x, y, value, false);
|
|
|
} else {
|
|
|
has_blanks = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
// Ok, the pieces given fit correctly with the sudoku constraints.
|
|
|
+ self.finalize_move();
|
|
|
|
|
|
if has_blanks {
|
|
|
// Verify that the remaining value == 0 positions have possibles.
|
|
@@ -826,17 +1014,19 @@ impl AnySolver {
|
|
|
for x in 0..self.board.width {
|
|
|
let value = self.board.get(x, y);
|
|
|
if value != 0 {
|
|
|
- self.process_move(x, y, value);
|
|
|
+ self.process_move(x, y, value, false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ self.finalize_move();
|
|
|
}
|
|
|
|
|
|
/// set (x,y) to value.
|
|
|
/// - This updates the board.
|
|
|
/// - This updates all the possibles (row,column,cell).
|
|
|
/// - Clears the possible for (x,y) [See process_move].
|
|
|
- pub fn set(&mut self, x: u8, y: u8, value: u8) {
|
|
|
+ /// TO FIX: I think set also needs finalize ... to pass to process_move.
|
|
|
+ pub fn set(&mut self, x: u8, y: u8, value: u8, finalize: bool) {
|
|
|
debug_assert!(
|
|
|
x < self.board.width && y < self.board.width,
|
|
|
"Expected ({}, {}) < {}",
|
|
@@ -853,7 +1043,7 @@ impl AnySolver {
|
|
|
|
|
|
self.board.set(x, y, value);
|
|
|
if value != 0 {
|
|
|
- self.process_move(x, y, value);
|
|
|
+ self.process_move(x, y, value, finalize);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -905,7 +1095,7 @@ impl AnySolver {
|
|
|
for value in available.into_iter() {
|
|
|
assert!(value != 0);
|
|
|
|
|
|
- self.set(x, y, value);
|
|
|
+ self.set(x, y, value, true);
|
|
|
// self.board.display();
|
|
|
// self.possible.display();
|
|
|
|
|
@@ -958,7 +1148,7 @@ impl AnySolver {
|
|
|
value = self.get(x, y);
|
|
|
}
|
|
|
|
|
|
- self.set(x, y, 0);
|
|
|
+ self.set(x, y, 0, true);
|
|
|
|
|
|
// clone, and solve by logic.
|
|
|
let mut puzcopy = self.clone();
|
|
@@ -970,7 +1160,8 @@ impl AnySolver {
|
|
|
}
|
|
|
|
|
|
// Not solvable, restore and return false.
|
|
|
- self.set(x, y, value);
|
|
|
+ // Since this failed, don't bother to finalize.
|
|
|
+ self.set(x, y, value, false);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -997,7 +1188,7 @@ impl AnySolver {
|
|
|
let value = self.possible.possible[i].iter().next().unwrap() + 1;
|
|
|
let pos = self.board.xy(i);
|
|
|
// println!("SET {}({},{})={}", i, pos.0 + 1, pos.1 + 1, value);
|
|
|
- self.set(pos.0, pos.1, value);
|
|
|
+ self.set(pos.0, pos.1, value, true);
|
|
|
found_something = true;
|
|
|
}
|
|
|
}
|
|
@@ -1067,7 +1258,7 @@ impl AnySolver {
|
|
|
let xy = this.board.xy(pos);
|
|
|
// this.possible.display();
|
|
|
// this.board.display();
|
|
|
- this.set(xy.0, xy.1, v + 1);
|
|
|
+ this.set(xy.0, xy.1, v + 1, true);
|
|
|
// println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v+1);
|
|
|
|
|
|
found_something = true;
|
|
@@ -1531,13 +1722,13 @@ mod tests {
|
|
|
println!("--- logic_test ---");
|
|
|
}
|
|
|
|
|
|
- #[ignore]
|
|
|
+ #[ignore]
|
|
|
#[test]
|
|
|
fn logic_test_pairs() {
|
|
|
let mut board = AnyBoard::new(3);
|
|
|
board.set(1, 0, 2);
|
|
|
board.set(2, 1, 3);
|
|
|
- board.set(2,2, 5);
|
|
|
+ board.set(2, 2, 5);
|
|
|
board.set(1, 6, 3);
|
|
|
board.set(0, 4, 4);
|
|
|
board.set(1, 7, 5);
|
|
@@ -1559,23 +1750,33 @@ mod tests {
|
|
|
╚═══╩═══╩═══╝
|
|
|
P = Possible pair (3,5)
|
|
|
|
|
|
- (1,1):1,6,7,8,9 (2,1): (3,1):1,3,4,5,6,7,8,9
|
|
|
- (1,2):1,6,7,8,9 (2,2):1,3,4,5,6,7,8,9 (3,2):1,3,4,5,6,7,8,9
|
|
|
- (1,3):1,6,7,8,9 (2,3):1,3,4,5,6,7,8,9 (3,3):1,3,4,5,6,7,8,9
|
|
|
- (1,4): (2,4):1,6,7,8,9 (3,4):1,2,6,7,8,9
|
|
|
- (1,5): (2,5):1,6,7,8,9 (3,5):1,2,6,7,8,9
|
|
|
- (1,6): (2,6):1,6,7,8,9 (3,6):1,2,6,7,8,9
|
|
|
- (1,7):1,2,6,7,8,9 (2,7):1,3,4,5,6,7,8,9 (3,7):1,2,3,4,5,6,7,8,9
|
|
|
- (1,8):1,2,6,7,8,9 (2,8):1,3,4,5,6,7,8,9 (3,8):1,2,3,4,5,6,7,8,9
|
|
|
- (1,9):1,2,6,7,8,9 (2,9):1,3,4,5,6,7,8,9 (3,9):1,2,3,4,5,6,7,8,9
|
|
|
- ^ This shows the logic bug.
|
|
|
- (3,7), (3,8) and (3,9) should not contain 2!
|
|
|
-
|
|
|
+ (1,1):1,6,7,8,9 (2,1): (3,1):1,4,6,7,8,9 (4,1):1,3,4,5,6,7,8,9 (5,1):1,3,4,5,6,7,8,9 (6,1):1,3,4,5,6,7,8,9 (7,1):1,3,4,5,6,7,8,9 (8,1):1,3,4,5,6,7,8,9 (9,1):1,3,4,5,6,7,8,9
|
|
|
+ (1,2):1,6,7,8,9 (2,2):1,4,6,7,8,9 (3,2): (4,2):1,2,4,5,6,7,8,9 (5,2):1,2,4,5,6,7,8,9 (6,2):1,2,4,5,6,7,8,9 (7,2):1,2,4,5,6,7,8,9 (8,2):1,2,4,5,6,7,8,9 (9,2):1,2,4,5,6,7,8,9
|
|
|
+ (1,3):1,6,7,8,9 (2,3):1,4,6,7,8,9 (3,3): (4,3):1,2,3,4,6,7,8,9 (5,3):1,2,3,4,6,7,8,9 (6,3):1,2,3,4,6,7,8,9 (7,3):1,2,3,4,6,7,8,9 (8,3):1,2,3,4,6,7,8,9 (9,3):1,2,3,4,6,7,8,9
|
|
|
+ (1,4):1,2,3,5,6,7,8,9 (2,4):1,6,7,8,9 (3,4):1,2,6,7,8,9 (4,4):1,2,3,4,5,6,7,8,9 (5,4):1,2,3,4,5,6,7,8,9 (6,4):1,2,3,4,5,6,7,8,9 (7,4):1,2,3,4,5,6,7,8,9 (8,4):1,2,3,4,5,6,7,8,9 (9,4):1,2,3,4,5,6,7,8,9
|
|
|
+ (1,5): (2,5):1,6,7,8,9 (3,5):1,2,6,7,8,9 (4,5):1,2,3,5,6,7,8,9 (5,5):1,2,3,5,6,7,8,9 (6,5):1,2,3,5,6,7,8,9 (7,5):1,2,3,5,6,7,8,9 (8,5):1,2,3,5,6,7,8,9 (9,5):1,2,3,5,6,7,8,9
|
|
|
+ (1,6):1,2,3,5,6,7,8,9 (2,6):1,6,7,8,9 (3,6):1,2,6,7,8,9 (4,6):1,2,3,4,5,6,7,8,9 (5,6):1,2,3,4,5,6,7,8,9 (6,6):1,2,3,4,5,6,7,8,9 (7,6):1,2,3,4,5,6,7,8,9 (8,6):1,2,3,4,5,6,7,8,9 (9,6):1,2,3,4,5,6,7,8,9
|
|
|
+ (1,7):1,2,6,7,8,9 (2,7): (3,7):1,2,4,6,7,8,9 (4,7):1,2,4,5,6,7,8,9 (5,7):1,2,4,5,6,7,8,9 (6,7):1,2,4,5,6,7,8,9 (7,7):1,2,4,5,6,7,8,9 (8,7):1,2,4,5,6,7,8,9 (9,7):1,2,4,5,6,7,8,9
|
|
|
+ (1,8):1,2,6,7,8,9 (2,8): (3,8):1,2,4,6,7,8,9 (4,8):1,2,3,4,6,7,8,9 (5,8):1,2,3,4,6,7,8,9 (6,8):1,2,3,4,6,7,8,9 (7,8):1,2,3,4,6,7,8,9 (8,8):1,2,3,4,6,7,8,9 (9,8):1,2,3,4,6,7,8,9
|
|
|
+ (1,9):1,2,6,7,8,9 (2,9):1,4,6,7,8,9 (3,9):1,2,4,6,7,8,9 (4,9):1,2,3,4,5,6,7,8,9 (5,9):1,2,3,4,5,6,7,8,9 (6,9):1,2,3,4,5,6,7,8,9 (7,9):1,2,3,4,5,6,7,8,9 (8,9):1,2,3,4,5,6,7,8,9 (9,9):1,2,3,4,5,6,7,8,9
|
|
|
+ ^ This shows the logic bug.
|
|
|
+ (1,4) and (1,6) should only be 3,5.
|
|
|
+ (3,7), (3,8), and (3,9) should not contain 2.
|
|
|
*/
|
|
|
|
|
|
- assert!(solver.validate_board());
|
|
|
+ println!("RIGHT HERE:");
|
|
|
+ println!("Pairs [cell index 3]: {:?}", solver.find_pairs(3));
|
|
|
+ solver.finalize_cell(3);
|
|
|
solver.possible.display();
|
|
|
|
|
|
+ /*
|
|
|
+ Pairs: [{1, 4, 8, 6, 7, 0, 5, 2}, {6, 0, 8, 5, 2}, {0, 6}, {}, {0, 6}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}, {1, 4, 8, 6, 7, 0, 5, 2}]
|
|
|
+ 3 and 5 are {0,6} !
|
|
|
+ */
|
|
|
+ // Is validate_board destroying our work?!?
|
|
|
+ // YES IT IS!
|
|
|
+ assert!(solver.validate_board());
|
|
|
+ solver.possible.display();
|
|
|
}
|
|
|
|
|
|
#[test]
|