|
@@ -71,8 +71,9 @@ impl AnyBoard {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- /// Calculate index position of (x,y)
|
|
|
|
#[inline]
|
|
#[inline]
|
|
|
|
+ #[must_use]
|
|
|
|
+ /// Calculate index position of (x,y)
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
x < self.width && y < self.width,
|
|
x < self.width && y < self.width,
|
|
@@ -84,8 +85,9 @@ impl AnyBoard {
|
|
x as usize + y as usize * self.width as usize
|
|
x as usize + y as usize * self.width as usize
|
|
}
|
|
}
|
|
|
|
|
|
- /// Return (x,y) position for given index.
|
|
|
|
|
|
+ #[must_use]
|
|
#[inline]
|
|
#[inline]
|
|
|
|
+ /// Return (x,y) position for given index.
|
|
pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
(
|
|
(
|
|
(idx % self.width as usize) as u8,
|
|
(idx % self.width as usize) as u8,
|
|
@@ -115,6 +117,7 @@ impl AnyBoard {
|
|
self.board[index] = value;
|
|
self.board[index] = value;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Get value at position (x,y)
|
|
/// Get value at position (x,y)
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
@@ -129,17 +132,20 @@ impl AnyBoard {
|
|
self.board[index]
|
|
self.board[index]
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Load from ksudoku format
|
|
/// Load from ksudoku format
|
|
/// - uses load_from_tld with specifics
|
|
/// - uses load_from_tld with specifics
|
|
pub fn load_ksudoku(&mut self, s: &str) -> Result<(), Box<dyn error::Error>> {
|
|
pub fn load_ksudoku(&mut self, s: &str) -> Result<(), Box<dyn error::Error>> {
|
|
self.load_from_tld('b', '_', s)
|
|
self.load_from_tld('b', '_', s)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Save to ksudoku format
|
|
/// Save to ksudoku format
|
|
pub fn save_ksudoku(&self) -> String {
|
|
pub fn save_ksudoku(&self) -> String {
|
|
self.save_to_tld('b', '_')
|
|
self.save_to_tld('b', '_')
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Load puzzle from string (top,left) going down.
|
|
/// Load puzzle from string (top,left) going down.
|
|
pub fn load_from_tld(
|
|
pub fn load_from_tld(
|
|
&mut self,
|
|
&mut self,
|
|
@@ -184,6 +190,7 @@ impl AnyBoard {
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Save puzzle to a string (top,left) going down.
|
|
/// Save puzzle to a string (top,left) going down.
|
|
pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
|
|
pub fn save_to_tld(&self, start_ch: char, blank: char) -> String {
|
|
let mut result = String::new();
|
|
let mut result = String::new();
|
|
@@ -208,6 +215,7 @@ impl AnyBoard {
|
|
result
|
|
result
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Load puzzle from string (top,left) going right.
|
|
/// Load puzzle from string (top,left) going right.
|
|
pub fn load_from_tlr(
|
|
pub fn load_from_tlr(
|
|
&mut self,
|
|
&mut self,
|
|
@@ -248,6 +256,7 @@ impl AnyBoard {
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Save puzzle to a string (top,left) going right.
|
|
/// Save puzzle to a string (top,left) going right.
|
|
pub fn save_to_tlr(&self, start_ch: char, blank: char) -> String {
|
|
pub fn save_to_tlr(&self, start_ch: char, blank: char) -> String {
|
|
let mut result = String::new();
|
|
let mut result = String::new();
|
|
@@ -328,6 +337,7 @@ impl AnyBoard {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Output board as strings.
|
|
/// Output board as strings.
|
|
/// - Uses 1-9 (for 9x9), and A-? for others.
|
|
/// - Uses 1-9 (for 9x9), and A-? for others.
|
|
/// - Used by tests to confirm the board is what we think it should be.
|
|
/// - Used by tests to confirm the board is what we think it should be.
|
|
@@ -368,6 +378,7 @@ impl AnyBoard {
|
|
true
|
|
true
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Solve by brute force
|
|
/// Solve by brute force
|
|
/// - Returns up to max # of solutions.
|
|
/// - Returns up to max # of solutions.
|
|
/// - Returns total solutions found, and vector of boards (up to max).
|
|
/// - Returns total solutions found, and vector of boards (up to max).
|
|
@@ -493,17 +504,25 @@ impl AnyPossible {
|
|
// NOTE: values range from 1..width. Bits range from 0..width-1
|
|
// NOTE: values range from 1..width. Bits range from 0..width-1
|
|
|
|
|
|
#[inline]
|
|
#[inline]
|
|
- pub fn set(&mut self, index: usize, value: usize, state: bool) {
|
|
|
|
|
|
+ /// Set value's state for given index.
|
|
|
|
+ /// - value range is from 1..=width.
|
|
|
|
+ /// - return true if state changed.
|
|
|
|
+ pub fn set(&mut self, index: usize, value: usize, state: bool) -> bool {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
index < self.max_index,
|
|
index < self.max_index,
|
|
"Index {} >= {}",
|
|
"Index {} >= {}",
|
|
index,
|
|
index,
|
|
self.max_index
|
|
self.max_index
|
|
);
|
|
);
|
|
|
|
+ let changed = self.possible[index].get(value-1) != state;
|
|
self.possible[index].set(value - 1, state);
|
|
self.possible[index].set(value - 1, state);
|
|
|
|
+ changed
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
#[inline]
|
|
#[inline]
|
|
|
|
+ /// Get state for given index and value.
|
|
|
|
+ /// - value range is from 1..=width.
|
|
pub fn get(&self, index: usize, value: usize) -> bool {
|
|
pub fn get(&self, index: usize, value: usize) -> bool {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
index < self.max_index,
|
|
index < self.max_index,
|
|
@@ -514,7 +533,9 @@ impl AnyPossible {
|
|
self.possible[index].get(value - 1)
|
|
self.possible[index].get(value - 1)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
#[inline]
|
|
#[inline]
|
|
|
|
+ /// Return index for given (x,y).
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
pub fn pos(&self, x: u8, y: u8) -> usize {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
x < self.width && y < self.width,
|
|
x < self.width && y < self.width,
|
|
@@ -525,8 +546,10 @@ impl AnyPossible {
|
|
);
|
|
);
|
|
x as usize + y as usize * self.width as usize
|
|
x as usize + y as usize * self.width as usize
|
|
}
|
|
}
|
|
- /// Return (x,y) position for given index.
|
|
|
|
|
|
+
|
|
|
|
+ #[must_use]
|
|
#[inline]
|
|
#[inline]
|
|
|
|
+ /// Return (x,y) position for given index.
|
|
pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
pub fn xy(&self, idx: usize) -> (u8, u8) {
|
|
debug_assert!(idx < self.max_index, "Index {} >= {}", idx, self.max_index);
|
|
debug_assert!(idx < self.max_index, "Index {} >= {}", idx, self.max_index);
|
|
(
|
|
(
|
|
@@ -535,6 +558,7 @@ impl AnyPossible {
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
/// Format all possible, finding max length.
|
|
/// Format all possible, finding max length.
|
|
/// - Return vec<string> of each item formatted.
|
|
/// - Return vec<string> of each item formatted.
|
|
/// - Return max length.
|
|
/// - Return max length.
|
|
@@ -553,6 +577,7 @@ impl AnyPossible {
|
|
(pos, max)
|
|
(pos, max)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
pub fn find_pairs(&mut self, cellgroup: &[usize]) -> (Vec<(Vec<u8>, Vec<u8>)>, bool) {
|
|
pub fn find_pairs(&mut self, cellgroup: &[usize]) -> (Vec<(Vec<u8>, Vec<u8>)>, bool) {
|
|
// result[value] = indexes where it is located?
|
|
// result[value] = indexes where it is located?
|
|
// Step 1: find values, and their position(s).
|
|
// Step 1: find values, and their position(s).
|
|
@@ -651,6 +676,34 @@ impl AnyPossible {
|
|
(pairs, possibles_updated)
|
|
(pairs, possibles_updated)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
|
|
+ /// Find positions where each value is possible.
|
|
|
|
+ pub fn find_value_pos(&self) -> Vec<GenBits<u32>> {
|
|
|
|
+ let zero = GenBits::<u32>(0);
|
|
|
|
+ let mut result = vec![zero; self.size as usize];
|
|
|
|
+
|
|
|
|
+ for index in 0..self.size {
|
|
|
|
+ if self.possible[index as usize] == zero {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ok, there's some value there.
|
|
|
|
+ // Would it be better to use the iterator here?
|
|
|
|
+ for v in self.possible[index as usize].iter() {
|
|
|
|
+ result[v as usize].set(index as usize, true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ for v in 0..self.width {
|
|
|
|
+ if self.possible[index as usize].get(v as usize +1) {
|
|
|
|
+ result[v as usize].set(index as usize, true);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
+ }
|
|
|
|
+ result
|
|
|
|
+ }
|
|
|
|
+
|
|
/// Display Possible
|
|
/// Display Possible
|
|
/// - Collect all of the possibles, with max length.
|
|
/// - Collect all of the possibles, with max length.
|
|
/// - Display formatted (using max length).
|
|
/// - Display formatted (using max length).
|
|
@@ -852,12 +905,16 @@ impl AnySolver {
|
|
let size = self.board.size;
|
|
let size = self.board.size;
|
|
let mut update: bool = false;
|
|
let mut update: bool = false;
|
|
|
|
|
|
- self.possible
|
|
|
|
|
|
+ let _pairs = self.possible
|
|
.find_pairs(self.group.group(Groups::Cell, cell_index));
|
|
.find_pairs(self.group.group(Groups::Cell, cell_index));
|
|
|
|
|
|
// println!("Finalize Cell Pairs: {:?}", self.possible.find_pairs(self.group.group(Groups::Cell, index)));
|
|
// println!("Finalize Cell Pairs: {:?}", self.possible.find_pairs(self.group.group(Groups::Cell, index)));
|
|
|
|
|
|
|
|
+ // get positions of possible numbers - for every cell ?
|
|
|
|
+
|
|
/*
|
|
/*
|
|
|
|
+ Great, I put my comments in a deprecated function...
|
|
|
|
+
|
|
Let me describe what I want to do here:
|
|
Let me describe what I want to do here:
|
|
|
|
|
|
╔═══╦═══╦═══╗
|
|
╔═══╦═══╦═══╗
|
|
@@ -886,6 +943,52 @@ impl AnySolver {
|
|
In cell 3, 2 must be in the last column.
|
|
In cell 3, 2 must be in the last column.
|
|
In cell 6, 2 must be in the first column. (3,7), (3,8) and (3,9) should not contain 2.
|
|
In cell 6, 2 must be in the first column. (3,7), (3,8) and (3,9) should not contain 2.
|
|
|
|
|
|
|
|
+ Top cell, 2 is in the middle (2,_). (it's not in the list of possible.)
|
|
|
|
+ Middle cell, 2 can only be in the last column. (3,*)
|
|
|
|
+ Bottom cell, 2 must be in (1,*).
|
|
|
|
+
|
|
|
|
+ How should I determine that?
|
|
|
|
+
|
|
|
|
+ 0.)
|
|
|
|
+ Build a table of numbers, and their possible positions. (used in the next steps)
|
|
|
|
+ I probably need some helpers to give me a row/column of a given position... as well.
|
|
|
|
+ row/column iterators ?
|
|
|
|
+
|
|
|
|
+ 1.)
|
|
|
|
+ In a given cell's column/row, if a number is possible in that cell's column/row, remove it from the other cell's row/column.
|
|
|
|
+ (Must be there, so remove it.)
|
|
|
|
+ (3,7), (3,8), (3,9) remove 2.
|
|
|
|
+
|
|
|
|
+ 2.)
|
|
|
|
+ In a given cell's column/row, if a number isn't possible in the same column/row in the other cells
|
|
|
|
+ -- remove that number as possible from that cell's other row/columns.
|
|
|
|
+
|
|
|
|
+ When updating the possibles - track which cells changed (so we can check/recheck just those).
|
|
|
|
+ After finalize, clear the changed flag (for that cell).
|
|
|
|
+
|
|
|
|
+ When allocating the blocks, possibly use the max value (size) -- this should keep the allocated blocks
|
|
|
|
+ the same (hopefully keep reusing the same size block of memory over and over)
|
|
|
|
+ -- or allocate it once and just reuse/init the same block over and over ? (reduce allocation/deallocations)
|
|
|
|
+
|
|
|
|
+ Also the reverse should be possible:
|
|
|
|
+ ╔═══╦═══╦═══╗
|
|
|
|
+ ║T ║ ║ ║
|
|
|
|
+ ║T ║ ║ ║
|
|
|
|
+ ║T ║ ║ ║
|
|
|
|
+ ╠═══╬═══╬═══╣
|
|
|
|
+ ║3tt║ ║ ║
|
|
|
|
+ ║4tt║ ║ ║
|
|
|
|
+ ║5tt║ ║ ║
|
|
|
|
+ ╠═══╬═══╬═══╣
|
|
|
|
+ ║ ║2 ║ ║
|
|
|
|
+ ║ ║ ║2 ║
|
|
|
|
+ ║7tt║ ║ ║
|
|
|
|
+ ╚═══╩═══╩═══╝
|
|
|
|
+
|
|
|
|
+ 2's possible in column 2 and 3. (for cells 3 and 6). Represented by 't'.
|
|
|
|
+ So, 2 must be in column 1 of cell 0. Represented by 'T'.
|
|
|
|
+
|
|
|
|
+
|
|
*/
|
|
*/
|
|
let (cell_x, cell_y) = self.group.xy(self.group.group(Groups::Cell, cell_index)[0]);
|
|
let (cell_x, cell_y) = self.group.xy(self.group.group(Groups::Cell, cell_index)[0]);
|
|
let x_range = cell_x..cell_x + self.board.size;
|
|
let x_range = cell_x..cell_x + self.board.size;
|
|
@@ -1071,6 +1174,7 @@ impl AnySolver {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ #[must_use]
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
pub fn get(&self, x: u8, y: u8) -> u8 {
|
|
debug_assert!(
|
|
debug_assert!(
|
|
x < self.board.width && y < self.board.width,
|
|
x < self.board.width && y < self.board.width,
|
|
@@ -1821,6 +1925,7 @@ mod tests {
|
|
solver.finalize_cell(3);
|
|
solver.finalize_cell(3);
|
|
solver.possible.display();
|
|
solver.possible.display();
|
|
}
|
|
}
|
|
|
|
+
|
|
#[ignore]
|
|
#[ignore]
|
|
#[test]
|
|
#[test]
|
|
fn logic_test_pairs() {
|
|
fn logic_test_pairs() {
|