|
@@ -9,10 +9,126 @@ const WIDTH: u8 = 9;
|
|
|
/// Size (width * height) of the board.
|
|
|
const MAX_SIZE: u8 = 81;
|
|
|
|
|
|
+// Use bitfields instead of HashSets.
|
|
|
+use bit_field::BitField;
|
|
|
+
|
|
|
+#[derive(Debug, Copy, Clone, PartialEq)]
|
|
|
+pub struct Possible(u16);
|
|
|
+
|
|
|
+pub const fn set_bits(bits: u8) -> u16 {
|
|
|
+ (1 << (bits )) - 1
|
|
|
+}
|
|
|
+
|
|
|
+impl Possible {
|
|
|
+ pub fn clear(&mut self) {
|
|
|
+ self.0 = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn set(&mut self, bit: u8, value: bool) {
|
|
|
+ // print!("{} set {}={}", self.0, bit, value);
|
|
|
+ self.0.set_bit((bit-1) as usize, value);
|
|
|
+ // println!("{}", self.0);
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn get(&self, bit: u8) -> bool {
|
|
|
+ self.0.get_bit((bit-1) as usize)
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn set_bits(&mut self, bits: u8) {
|
|
|
+ self.0 = set_bits(bits);
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn count_set(&self) -> u8 {
|
|
|
+ let mut count = 0;
|
|
|
+ for i in 1..u16::BIT_LENGTH {
|
|
|
+ if self.get(i as u8) {
|
|
|
+ count += 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ count
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+struct PossibleIterator<'a> {
|
|
|
+ possible: &'a Possible,
|
|
|
+ index: u8,
|
|
|
+}
|
|
|
+
|
|
|
+impl Possible {
|
|
|
+ fn iter(&self) -> PossibleIterator {
|
|
|
+ PossibleIterator {
|
|
|
+ possible: self,
|
|
|
+ index: 1,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a> Iterator for PossibleIterator<'a> {
|
|
|
+ type Item = u8;
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<u8> {
|
|
|
+ while (self.index <= u16::BIT_LENGTH as u8) && (!self.possible.get(self.index)) {
|
|
|
+ self.index += 1;
|
|
|
+ // println!("index = {}", self.index);
|
|
|
+ }
|
|
|
+ if self.index >= u16::BIT_LENGTH as u8 {
|
|
|
+ None
|
|
|
+ } else {
|
|
|
+ self.index += 1;
|
|
|
+ Some(self.index - 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use crate::sudoku::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn check_possible_bitset() {
|
|
|
+ let mut p = Possible(0);
|
|
|
+
|
|
|
+ p.clear();
|
|
|
+
|
|
|
+ for i in 0..9 {
|
|
|
+ let mut result = p.get(i);
|
|
|
+ assert_eq!(result, false);
|
|
|
+ p.set(i, true);
|
|
|
+ result = p.get(i);
|
|
|
+ assert_eq!(result, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn check_possible_iter() {
|
|
|
+ let mut p = Possible(0);
|
|
|
+ p.set(3, true);
|
|
|
+ p.set(5, true);
|
|
|
+ p.set(6, true);
|
|
|
+ assert_eq!(3, p.count_set());
|
|
|
+ let values: Vec<u8> = p.iter().collect();
|
|
|
+ assert_eq!(values, vec!(3, 5, 6));
|
|
|
+ assert_eq!(3, p.count_set());
|
|
|
+ p.set(0, true);
|
|
|
+ assert_eq!(4, p.count_set());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn check_bits() {
|
|
|
+ let mut p = Possible(set_bits(5));
|
|
|
+ for i in 0..6 {
|
|
|
+ let result = p.get(i);
|
|
|
+ assert_eq!(result, true);
|
|
|
+ }
|
|
|
+ assert_eq!(p.get(6), false);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
#[derive(Debug)]
|
|
|
pub struct Sudoku {
|
|
|
pub board: [u8; MAX_SIZE as usize],
|
|
|
- pub possible: [HashSet<u8>; MAX_SIZE as usize],
|
|
|
+ // pub possible: [HashSet<u8>; MAX_SIZE as usize],
|
|
|
+ pub possible: [Possible; MAX_SIZE as usize],
|
|
|
}
|
|
|
|
|
|
/// Translate x,y to position in board.
|
|
@@ -42,10 +158,12 @@ let arr: [Vec<u32>; 10] = [(); 10].map(|_| Vec::with_capacity(100));
|
|
|
impl Sudoku {
|
|
|
pub fn new() -> Self {
|
|
|
// let b : HashSet<u8> = HashSet::from_iter(1..=9);
|
|
|
+ let initial: Possible = Possible(set_bits(9));
|
|
|
|
|
|
let s = Sudoku {
|
|
|
board: [0; MAX_SIZE as usize],
|
|
|
- possible: [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9)),
|
|
|
+ // possible: [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9)),
|
|
|
+ possible: [initial; MAX_SIZE as usize],
|
|
|
// possible: [HashSet::from_iter(1..=9); MAX_SIZE as usize],
|
|
|
// possible: [[0; SIZE as usize]; MAX_SIZE as usize],
|
|
|
// possible: [(0..MAX_SIZE).map( |_| (1..=9).collect())],
|
|
@@ -55,9 +173,12 @@ impl Sudoku {
|
|
|
}
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
+ let initial = Possible(set_bits(9));
|
|
|
+
|
|
|
for x in 0..MAX_SIZE {
|
|
|
self.board[x as usize] = 0;
|
|
|
- self.possible = [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9));
|
|
|
+ self.possible[x as usize] = initial;
|
|
|
+ // self.possible = [(); MAX_SIZE as usize].map(|_| HashSet::from_iter(1..=9));
|
|
|
/*
|
|
|
self.possible[x as usize].clear();
|
|
|
for i in 1..=9 {
|
|
@@ -143,19 +264,22 @@ impl Sudoku {
|
|
|
// g.for_row(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
+ // self.possible[g as usize].take(&value);
|
|
|
}
|
|
|
g = for_column(x);
|
|
|
// g.for_column(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
+ // self.possible[g as usize].take(&value);
|
|
|
}
|
|
|
g = for_cell(which_cell(x, y));
|
|
|
// g.for_block(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
+ // self.possible[g as usize].take(&value);
|
|
|
}
|
|
|
self.possible[pos(x, y) as usize].clear();
|
|
|
}
|
|
@@ -167,19 +291,19 @@ impl Sudoku {
|
|
|
g.for_row(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
}
|
|
|
|
|
|
g.for_column(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
}
|
|
|
|
|
|
g.for_block(x, y);
|
|
|
for g in g.0 {
|
|
|
// remove value from these sets.
|
|
|
- self.possible[g as usize].take(&value);
|
|
|
+ self.possible[g as usize].set(value, false);
|
|
|
}
|
|
|
self.possible[pos(x, y) as usize].clear();
|
|
|
}
|
|
@@ -213,10 +337,12 @@ impl Sudoku {
|
|
|
pub fn display_possible(&self) {
|
|
|
for y in 0..WIDTH {
|
|
|
for x in 0..WIDTH {
|
|
|
+ // print!("p={:?}", self.possible[pos(x, y) as usize]);
|
|
|
let mut possible = String::new();
|
|
|
|
|
|
for p in self.possible[pos(x, y) as usize].iter() {
|
|
|
- possible += format!("{}", p).as_str();
|
|
|
+ // print!("{},", p);
|
|
|
+ possible += format!("{},", p).as_str();
|
|
|
}
|
|
|
|
|
|
// for i in 0..SIZE {
|
|
@@ -243,9 +369,9 @@ impl Sudoku {
|
|
|
let mut found_something = false;
|
|
|
|
|
|
for i in 0..MAX_SIZE {
|
|
|
- if self.possible[i as usize].len() == 1 {
|
|
|
+ if self.possible[i as usize].count_set() == 1 {
|
|
|
// Get the value
|
|
|
- let value = self.possible[i as usize].iter().next().cloned().unwrap();
|
|
|
+ let value = self.possible[i as usize].iter().next().unwrap();
|
|
|
// Found one!
|
|
|
println!("Set1 {:?} to {}", xy(i), value);
|
|
|
self.set(xy(i).0, xy(i).1, value);
|
|
@@ -254,14 +380,17 @@ impl Sudoku {
|
|
|
}
|
|
|
|
|
|
let mut g = Group::new();
|
|
|
- let mut values: HashSet<u8> = HashSet::new();
|
|
|
+ let mut values = Possible(0); // HashSet<u8> = HashSet::new();
|
|
|
|
|
|
let mut group_process = |this: &mut Self, grp: &Group| {
|
|
|
// 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]);
|
|
|
- values.extend(&this.possible[grp.0[gidx as usize] as usize]);
|
|
|
+ for v in this.possible[grp.0[gidx as usize] 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]);
|
|
|
}
|
|
|
|
|
@@ -272,7 +401,8 @@ impl Sudoku {
|
|
|
let mut count = 0;
|
|
|
let mut pos = 0;
|
|
|
for gidx in 0..WIDTH {
|
|
|
- if this.possible[grp.0[gidx as usize] as usize].contains(&v) {
|
|
|
+ if this.possible[grp.0[gidx as usize] as usize].get(v) {
|
|
|
+ // if this.possible[grp.0[gidx as usize] as usize].contains(&v) {
|
|
|
count += 1;
|
|
|
pos = grp.0[gidx as usize];
|
|
|
if count > 1 {
|
|
@@ -284,7 +414,7 @@ impl Sudoku {
|
|
|
// don't need this, it was v!
|
|
|
// let value = this.possible[pos as usize].iter().next().cloned().unwrap();
|
|
|
println!("Set2 {:?} to {}", xy(pos), v);
|
|
|
- this.set(xy(pos).0, xy(pos).1, *v);
|
|
|
+ this.set(xy(pos).0, xy(pos).1, v);
|
|
|
found_something = true;
|
|
|
}
|
|
|
}
|
|
@@ -314,40 +444,45 @@ impl Sudoku {
|
|
|
|
|
|
for gidx in 0..WIDTH - 1 {
|
|
|
let gpos = g.0[gidx as usize];
|
|
|
- if self.possible[gpos as usize].len() == 2 {
|
|
|
+ if self.possible[gpos as usize].count_set() == 2 {
|
|
|
// Found a pair
|
|
|
for fidx in gidx + 1..WIDTH {
|
|
|
let fpos = g.0[fidx as usize];
|
|
|
- if self.possible[fpos as usize].len() == 2 {
|
|
|
+ if self.possible[fpos as usize].count_set() == 2 {
|
|
|
// Ok, there's another pair
|
|
|
- if self.possible[gpos as usize].is_subset(&self.possible[fpos as usize])
|
|
|
- {
|
|
|
+ // if self.possible[gpos as usize].is_subset(&self.possible[fpos as usize])
|
|
|
+ if self.possible[gpos as usize] == self.possible[fpos as usize] {
|
|
|
// Ok, they have the same values!
|
|
|
// Ok, remove the items in the pair from the cell.
|
|
|
// Don't touch the gpos/fpos records. Keep those!
|
|
|
let mut values: [u8; 2] = [0, 0];
|
|
|
let mut vpos = 0;
|
|
|
for z in self.possible[gpos as usize].iter() {
|
|
|
- values[vpos] = *z;
|
|
|
+ values[vpos] = z;
|
|
|
vpos += 1;
|
|
|
}
|
|
|
|
|
|
let mut pair_removed = false;
|
|
|
|
|
|
+ // It isn't currently possible to tell if anything was removed...
|
|
|
+
|
|
|
for remove in 0..WIDTH {
|
|
|
if (gidx == remove) || (fidx == remove) {
|
|
|
continue;
|
|
|
}
|
|
|
// Ok, these aren't the ones to save, so:
|
|
|
let rpos = g.0[remove as usize];
|
|
|
- if self.possible[rpos as usize].take(&values[0]).is_some() {
|
|
|
- found_something = true;
|
|
|
- pair_removed = true;
|
|
|
- };
|
|
|
- if self.possible[rpos as usize].take(&values[1]).is_some() {
|
|
|
+ self.possible[rpos as usize].set(values[0], false); /* ).is_some() {
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ };
|
|
|
+ */
|
|
|
+ self.possible[rpos as usize].set(values[1], false);
|
|
|
+ /*.is_some() {
|
|
|
found_something = true;
|
|
|
pair_removed = true;
|
|
|
};
|
|
|
+ */
|
|
|
}
|
|
|
|
|
|
if pair_removed {
|
|
@@ -369,25 +504,29 @@ impl Sudoku {
|
|
|
|
|
|
vpos = 0;
|
|
|
for z in self.possible[gpos as usize].iter() {
|
|
|
- values[vpos] = *z;
|
|
|
+ values[vpos] = z;
|
|
|
vpos += 1;
|
|
|
}
|
|
|
for remove in 0..WIDTH {
|
|
|
if (remove == xy(gpos).1) || (remove == xy(fpos).1) {
|
|
|
continue;
|
|
|
}
|
|
|
- if self.possible[pos(column, remove) as usize]
|
|
|
- .take(&values[0])
|
|
|
+ self.possible[pos(column, remove) as usize]
|
|
|
+ .set(values[0], false);
|
|
|
+ /* v
|
|
|
.is_some()
|
|
|
{
|
|
|
found_something = true;
|
|
|
};
|
|
|
- if self.possible[pos(column, remove) as usize]
|
|
|
- .take(&values[1])
|
|
|
+ */
|
|
|
+ self.possible[pos(column, remove) as usize]
|
|
|
+ .set(values[1], false);
|
|
|
+ /*
|
|
|
.is_some()
|
|
|
{
|
|
|
found_something = true;
|
|
|
};
|
|
|
+ */
|
|
|
}
|
|
|
}
|
|
|
if xy(gpos).1 == xy(fpos).1 {
|
|
@@ -396,25 +535,29 @@ impl Sudoku {
|
|
|
|
|
|
vpos = 0;
|
|
|
for z in self.possible[gpos as usize].iter() {
|
|
|
- values[vpos] = *z;
|
|
|
+ values[vpos] = z;
|
|
|
vpos += 1;
|
|
|
}
|
|
|
for remove in 0..WIDTH {
|
|
|
if (remove == xy(gpos).0) || (remove == xy(fpos).0) {
|
|
|
continue;
|
|
|
}
|
|
|
- if self.possible[pos(remove, row) as usize]
|
|
|
- .take(&values[0])
|
|
|
+ self.possible[pos(remove, row) as usize]
|
|
|
+ .set(values[0], false);
|
|
|
+ /*
|
|
|
.is_some()
|
|
|
{
|
|
|
found_something = true;
|
|
|
};
|
|
|
- if self.possible[pos(remove, row) as usize]
|
|
|
- .take(&values[1])
|
|
|
+ */
|
|
|
+ self.possible[pos(remove, row) as usize]
|
|
|
+ .set(values[1], false);
|
|
|
+ /*
|
|
|
.is_some()
|
|
|
{
|
|
|
found_something = true;
|
|
|
};
|
|
|
+ */
|
|
|
}
|
|
|
}
|
|
|
}
|