|
@@ -445,7 +445,7 @@ impl AnyBoard {
|
|
|
solutions.reserve(max as usize);
|
|
|
workset.brute_force(&mut total_solutions, &mut solutions, &groups);
|
|
|
|
|
|
- /*
|
|
|
+ /*
|
|
|
if solutions.len() > 0 {
|
|
|
println!("*** A Solution:");
|
|
|
solutions[0].display();
|
|
@@ -694,20 +694,21 @@ impl AnySolver {
|
|
|
);
|
|
|
|
|
|
let mut g = self.group.row(y);
|
|
|
- let value: usize = value as usize;
|
|
|
+ let val: usize = value as usize;
|
|
|
for g in g {
|
|
|
- self.possible.set(*g, value, false);
|
|
|
+ self.possible.set(*g, val, false);
|
|
|
}
|
|
|
g = self.group.column(x);
|
|
|
for g in g {
|
|
|
- self.possible.set(*g, value, false);
|
|
|
+ self.possible.set(*g, val, false);
|
|
|
}
|
|
|
g = self.group.cell(self.group.which_cell(x, y));
|
|
|
for g in g {
|
|
|
- self.possible.set(*g, value, false);
|
|
|
+ self.possible.set(*g, val, false);
|
|
|
}
|
|
|
let idx = self.possible.pos(x, y);
|
|
|
self.possible.possible[idx] = GenBits::<u32>(0); // .clear();
|
|
|
+ self.board.board[idx] = value;
|
|
|
}
|
|
|
|
|
|
/// Validate the board
|
|
@@ -867,23 +868,182 @@ impl AnySolver {
|
|
|
/// - Call solve until it returns false.
|
|
|
/// - It might not be solved (if guessing is required).
|
|
|
pub fn solve_logic(&mut self) -> bool {
|
|
|
+ self.reset_possible();
|
|
|
+ // self.board.display();
|
|
|
+ // self.possible.display();
|
|
|
+
|
|
|
// Pass 1: Look for singles in the possible sets.
|
|
|
let mut found_something = false;
|
|
|
+ let mut pass1 = false;
|
|
|
|
|
|
- for i in 0 .. self.possible.max_index {
|
|
|
- if self.board.board[i] != 0 {
|
|
|
- // Skip, if position already filled.
|
|
|
- continue;
|
|
|
+ // Repeat pass 1 until all have been found.
|
|
|
+ let mut pass1_again = true;
|
|
|
+
|
|
|
+ let width = self.group.width;
|
|
|
+ let grp = self.group.clone();
|
|
|
+ let mut pass2 = false;
|
|
|
+
|
|
|
+ while pass1_again {
|
|
|
+ // Pass 1 - look for singles.
|
|
|
+
|
|
|
+ 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() + 1;
|
|
|
+ let pos = self.board.xy(i);
|
|
|
+ // println!("SET {}({},{})={}", i, pos.0 + 1, pos.1 + 1, value);
|
|
|
+ self.set(pos.0, pos.1, value);
|
|
|
+ found_something = true;
|
|
|
+ }
|
|
|
}
|
|
|
- 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;
|
|
|
+
|
|
|
+ if found_something {
|
|
|
+ // We found something this pass.
|
|
|
+ // - set pass1 to true (pass 1 found something)
|
|
|
+ // - reset found_something so we can try again.
|
|
|
+ pass1 = true;
|
|
|
+ found_something = false;
|
|
|
+ continue;
|
|
|
+ } else {
|
|
|
+ // Nothing found with this run of pass 1.
|
|
|
+
|
|
|
+ // Pass 2 - Look for singles within the groups.
|
|
|
+
|
|
|
+ found_something = false;
|
|
|
+
|
|
|
+ let mut values = Bits(0); // HashSet<u8> = HashSet::new();
|
|
|
+
|
|
|
+ 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]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // println!("values {:?}", values);
|
|
|
+
|
|
|
+ // Now, check for singles.
|
|
|
+ for v in values.iter() {
|
|
|
+ // println!("Check Value: {}", v);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ } else {
|
|
|
+ // print!(" IDX {} POS {} ", gidx, pos);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if count == 1 {
|
|
|
+ // don't need this, it was v!
|
|
|
+ // let value = this.possible[pos as usize].iter().next().cloned().unwrap();
|
|
|
+ let xy = this.board.xy(pos);
|
|
|
+ // this.possible.display();
|
|
|
+ // this.board.display();
|
|
|
+ this.set(xy.0, xy.1, v+1);
|
|
|
+ // println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v+1);
|
|
|
+
|
|
|
+ found_something = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // Change to 0..WIDTH ... Keep it simple.
|
|
|
+ for i in 0..width {
|
|
|
+ // println!("Column {i}:");
|
|
|
+ let mut g = grp.column(i);
|
|
|
+ group_process(self, g);
|
|
|
+ // println!("Row {i}:");
|
|
|
+ g = grp.row(i);
|
|
|
+ group_process(self, g);
|
|
|
+ // println!("Cell {i}:");
|
|
|
+ g = grp.cell(i);
|
|
|
+ group_process(self, g);
|
|
|
+ }
|
|
|
+
|
|
|
+ if found_something == true {
|
|
|
+ pass2 = true;
|
|
|
+ // Ok, pass 2 found something.
|
|
|
+ found_something = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // - If pass1 set, reset the found_something (because it
|
|
|
+ // did find something)
|
|
|
+ // - Clear the pass1_again flag, we're done with pass 1.
|
|
|
+ if pass1 {
|
|
|
+ pass1_again = false;
|
|
|
+ found_something = true;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // After changing Pass1 to loop, I don't think I need to go over the
|
|
|
+ // groups now. Besides, possible should be covering all the groups
|
|
|
+ // anyway!
|
|
|
+
|
|
|
+ /*
|
|
|
+ println!("Set2:");
|
|
|
+ self.board.display();
|
|
|
+ self.possible.display();
|
|
|
+ */
|
|
|
+
|
|
|
+ // This is a mess here. It looks obsoleted by the above pass1 loop.
|
|
|
+
|
|
|
+ // No, it isn't.
|
|
|
+
|
|
|
+ /*
|
|
|
+ Set2:
|
|
|
+╔═══╦═══╦═══╗
|
|
|
+║ 1║7 8║3 ║
|
|
|
+║ 73║ ║68 ║
|
|
|
+║286║934║751║
|
|
|
+╠═══╬═══╬═══╣
|
|
|
+║ ║1 7║ ║
|
|
|
+║ 2 ║ ║ 9 ║
|
|
|
+║ 14║ ║86 ║
|
|
|
+╠═══╬═══╬═══╣
|
|
|
+║ 8║3 1║9 ║
|
|
|
+║ ║ ║ ║
|
|
|
+║432║879║516║
|
|
|
+╚═══╩═══╩═══╝
|
|
|
+(1,1):59 (2,1):459 (3,1): (4,1): (5,1):256 (6,1): (7,1): (8,1):24 (9,1):249
|
|
|
+(1,2):59 (2,2): (3,2): (4,2):25 (5,2):125 (6,2):25 (7,2): (8,2): (9,2):249
|
|
|
+(1,3): (2,3): (3,3): (4,3): (5,3): (6,3): (7,3): (8,3): (9,3):
|
|
|
+(1,4):35689 (2,4):569 (3,4):59 (4,4): (5,4):245689 (6,4): (7,4):24 (8,4):234 (9,4):2345
|
|
|
+(1,5):35678 (2,5): (3,5):57 (4,5):456 (5,5):4568 (6,5):356 (7,5):14 (8,5): (9,5):3457
|
|
|
+(1,6):3579 (2,6): (3,6): (4,6):25 (5,6):259 (6,6):235 (7,6): (8,6): (9,6):2357
|
|
|
+(1,7):567 (2,7):56 (3,7): (4,7): (5,7):2456 (6,7): (7,7): (8,7):247 (9,7):247
|
|
|
+(1,8):15679 (2,8):569 (3,8):579 (4,8):2456 (5,8):2456 (6,8):256 (7,8):24 (8,8):2347 (9,8):23478
|
|
|
+(1,9): (2,9): (3,9): (4,9): (5,9): (6,9): (7,9): (8,9): (9,9):
|
|
|
+
|
|
|
+ See:
|
|
|
+ (2,1) only has a 4 in cell/column.
|
|
|
+ (5,2) only has a 1 in cell.
|
|
|
+ (7,5) only has a 1 in column.
|
|
|
+ (1,8) only has a 1 (rows/column).
|
|
|
+ (9,8) only has 8.
|
|
|
+ */
|
|
|
+
|
|
|
+ /*
|
|
|
let width = self.group.width;
|
|
|
let grp = self.group.clone();
|
|
|
let mut values = Bits(0); // HashSet<u8> = HashSet::new();
|
|
@@ -900,32 +1060,36 @@ impl AnySolver {
|
|
|
// println!("now : {:?}", this.possible[grp.items[gidx as usize] as usize]);
|
|
|
}
|
|
|
|
|
|
- // println!("values {:?}", values);
|
|
|
+ println!("values {:?}", values);
|
|
|
|
|
|
// Now, check for singles.
|
|
|
for v in values.iter() {
|
|
|
+ println!("Check Value: {}", v);
|
|
|
+
|
|
|
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) {
|
|
|
+
|
|
|
+ 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;
|
|
|
+ } else {
|
|
|
+ print!(" IDX {} POS {} ", gidx, pos);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
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.possible.display();
|
|
|
+ this.board.display();
|
|
|
this.set(xy.0, xy.1, v);
|
|
|
+ println!("SET {} ({},{}) = {}", pos, xy.0 + 1, xy.1 + 1, v);
|
|
|
+
|
|
|
found_something = true;
|
|
|
}
|
|
|
}
|
|
@@ -933,10 +1097,13 @@ impl AnySolver {
|
|
|
|
|
|
// Change to 0..WIDTH ... Keep it simple.
|
|
|
for i in 0..width {
|
|
|
+ println!("Column {i}:");
|
|
|
let mut g = grp.column(i);
|
|
|
group_process(self, g);
|
|
|
+ println!("Row {i}:");
|
|
|
g = grp.row(i);
|
|
|
group_process(self, g);
|
|
|
+ println!("Cell {i}:");
|
|
|
g = grp.cell(i);
|
|
|
group_process(self, g);
|
|
|
}
|
|
@@ -944,9 +1111,148 @@ impl AnySolver {
|
|
|
if found_something {
|
|
|
return found_something;
|
|
|
}
|
|
|
+ */
|
|
|
+ return false;
|
|
|
+
|
|
|
+
|
|
|
+ let width = self.group.width;
|
|
|
+ let grp = self.group.clone();
|
|
|
|
|
|
// Pair processing.
|
|
|
+ for i in 0..width {
|
|
|
+ let mut g = grp.cell(i);
|
|
|
|
|
|
+ for gidx in 0..WIDTH - 1 {
|
|
|
+ let gpos = g[gidx as usize];
|
|
|
+ if self.possible.possible[gpos as usize].count_set() == 2 {
|
|
|
+ // Found a pair
|
|
|
+ for fidx in gidx + 1..width {
|
|
|
+ let fpos = g[fidx as usize];
|
|
|
+ if self.possible.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.possible[gpos] == self.possible.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.possible[gpos].iter() {
|
|
|
+ values[vpos] = z + 1;
|
|
|
+ vpos += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut pair_removed = false;
|
|
|
+
|
|
|
+ // Check to see 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[remove as usize];
|
|
|
+ if self.possible.possible[rpos].get(values[0] as usize) {
|
|
|
+ self.possible.possible[rpos].set(values[0] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.possible.possible[rpos].get(values[1] as usize) {
|
|
|
+ self.possible.possible[rpos].set(values[1] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check the x's and y's to see if we can also process a row/column too.
|
|
|
+ if self.possible.xy(gpos).0 == self.possible.xy(fpos).0 {
|
|
|
+ // Matching X - process column
|
|
|
+ let column = xy(gpos).0;
|
|
|
+
|
|
|
+ vpos = 0;
|
|
|
+ for z in self.possible.possible[gpos].iter() {
|
|
|
+ values[vpos] = z + 1;
|
|
|
+ vpos += 1;
|
|
|
+ }
|
|
|
+ for remove in 0..WIDTH {
|
|
|
+ if (remove == xy(gpos).1) || (remove == xy(fpos).1) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ let pos = self.possible.pos(column, remove);
|
|
|
+
|
|
|
+ if self.possible.possible[pos].get(values[0] as usize) {
|
|
|
+ self.possible.possible[pos]
|
|
|
+ .set(values[0] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.possible.possible[pos].get(values[1] as usize) {
|
|
|
+ self.possible.possible[pos]
|
|
|
+ .set(values[1] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.possible.xy(gpos).1 == self.possible.xy(fpos).1 {
|
|
|
+ // Matching Y - process row
|
|
|
+ let row = self.possible.xy(gpos).1;
|
|
|
+
|
|
|
+ vpos = 0;
|
|
|
+ for z in self.possible.possible[gpos].iter() {
|
|
|
+ values[vpos] = z + 1;
|
|
|
+ vpos += 1;
|
|
|
+ }
|
|
|
+ for remove in 0..width {
|
|
|
+ if (remove == self.possible.xy(gpos).0)
|
|
|
+ || (remove == self.possible.xy(fpos).0)
|
|
|
+ {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ let pos = self.possible.pos(remove, row);
|
|
|
+
|
|
|
+ if self.possible.possible[pos].get(values[0] as usize) {
|
|
|
+ self.possible.possible[pos]
|
|
|
+ .set(values[0] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.possible.possible[pos].get(values[1] as usize) {
|
|
|
+ self.possible.possible[pos]
|
|
|
+ .set(values[1] as usize, false);
|
|
|
+ found_something = true;
|
|
|
+ pair_removed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ if pair_removed {
|
|
|
+ if debug {
|
|
|
+ println!(
|
|
|
+ "Pair found! {} {}: {} {:?} and {} {:?} !",
|
|
|
+ gidx,
|
|
|
+ fidx,
|
|
|
+ gpos,
|
|
|
+ xy(gpos),
|
|
|
+ fpos,
|
|
|
+ xy(fpos)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+ */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
found_something
|
|
|
}
|
|
|
}
|
|
@@ -984,6 +1290,15 @@ mod tests {
|
|
|
// board.display();
|
|
|
let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(solver.validate_board());
|
|
|
+
|
|
|
+ // solver.solve_logic();
|
|
|
+
|
|
|
+ while (solver.solve_logic()) {
|
|
|
+ solver.board.display();
|
|
|
+ }
|
|
|
+
|
|
|
+ assert!(solver.validate_board());
|
|
|
+ assert!(solver.board.complete());
|
|
|
|
|
|
board = AnyBoard::new(4);
|
|
|
let result = board.load_from_tld('b', '_', "_bo_j_m_f__dp__ge_h_pcfdo___q__n___qio______df___f__l___hpnm_i___obig_p_qhl__k_m_dq_cn______o_g_p_____bi_kc__jn______fo____gi______eb____jd______jk__ml_bn_____i_m_b______oq_nj_d_n__jck_m_fgbq___i_medp___n__b___dg______qjk___j__p___fgohl_d_qo__mq__g_d_p_le_");
|
|
@@ -1014,6 +1329,15 @@ mod tests {
|
|
|
|
|
|
let mut solver = AnySolver::new_from(&board);
|
|
|
assert!(solver.validate_board());
|
|
|
+ solver.board.display();
|
|
|
+
|
|
|
+ while (solver.solve_logic()) {
|
|
|
+ solver.board.display();
|
|
|
+ }
|
|
|
+
|
|
|
+ assert!(solver.validate_board());
|
|
|
+ solver.board.display();
|
|
|
+ assert!(solver.board.complete());
|
|
|
|
|
|
let mut board: AnyBoard = AnyBoard::new(5);
|
|
|
let result = board.load_from_tld('b', '_', "_m_kzg____h_s_n____ftd_u_u_dh_____f_b_t_w_____yg_c_rl_o_tdhy__m__uvig_w_sk_eg___p_q_jikouys_r_d___mlq_t_sb_emcwg_dlzyo_kp_i_ng__ir_b_fp_vhz_ce_y_jm__w__m__o_k_xul_qbt_d_s__e____otv_dhegn___mfkpz_blr____s_dv_n_mjx_ckg_w_bo_p___kqyelwjcz_____nxumoisdh_z__fp_vbi_______dkx_eg__r_y_mlwf_u__q_i__o_chdv_j_i_he_r_____________p_zl_k_d_vbjh_y__e_p__s_tguc_q_s__qj_kpn_______ufw_hx__i_hvntirfxw_____lbckympjg___u_kz_m_bfn_yvx_h_ir_o____rgm_otlnx___ipfes_kwc____p__c_v_ugh_krj_m_w__x__x__ci_j_qk_mpo_dr_u_zb__ht_i_qe_wjvcy_bhkzx_ng_u_syv___u_c_hsfrlqo_t_e___pj_cn_h_slzr__j__mqgp_y_vd_m_bs_____t_o_n_h_____ez_f_e_ufd____p_g_z____cqr_x_");
|
|
@@ -1328,8 +1652,8 @@ impl<'a> BoardPossible<'a> {
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
- ___ _ _ ____ _
|
|
|
- / _ \| | __| | / ___|___ __| | ___
|
|
|
+ ___ _ _ ____ _
|
|
|
+ / _ \| | __| | / ___|___ __| | ___
|
|
|
| | | | |/ _` | | | / _ \ / _` |/ _ \
|
|
|
| |_| | | (_| | | |__| (_) | (_| | __/
|
|
|
\___/|_|\__,_| \____\___/ \__,_|\___|
|