|  | @@ -5,15 +5,9 @@ use crate::group::*;
 | 
	
		
			
				|  |  |  // use std::collections::HashSet;
 | 
	
		
			
				|  |  |  use std::string::String;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -const SIZE: u8 = 3;
 | 
	
		
			
				|  |  | -/// Width of the sudoku board.
 | 
	
		
			
				|  |  | -const WIDTH: u8 = SIZE * SIZE;
 | 
	
		
			
				|  |  | -/// Size (width * height) of the board.
 | 
	
		
			
				|  |  | -const MAX_SIZE: u8 = WIDTH * WIDTH; // 81;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  extern crate rand_chacha;
 | 
	
		
			
				|  |  |  // use rand::prelude::*;
 | 
	
		
			
				|  |  | -use rand::distributions::{Distribution, Uniform};
 | 
	
		
			
				|  |  | +// use rand::distributions::{Distribution, Uniform};
 | 
	
		
			
				|  |  |  use rand::seq::SliceRandom;
 | 
	
		
			
				|  |  |  use rand_chacha::rand_core::SeedableRng;
 | 
	
		
			
				|  |  |  use rand_chacha::ChaCha20Rng;
 | 
	
	
		
			
				|  | @@ -22,81 +16,6 @@ use rand_chacha::ChaCha20Rng;
 | 
	
		
			
				|  |  |  use std::error;
 | 
	
		
			
				|  |  |  use std::fmt;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Used bo calculate_possible to return the solutions.
 | 
	
		
			
				|  |  | -pub type SudokuBoard = [u8; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -pub type Possible = [Bits; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -pub type SudokuPossible = [Bits; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -*/
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#[derive(Debug, Clone, Copy)]
 | 
	
		
			
				|  |  | -pub struct Board([u8; MAX_SIZE as usize]);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -impl Board {
 | 
	
		
			
				|  |  | -    pub fn new() -> Self {
 | 
	
		
			
				|  |  | -        let s = Self {
 | 
	
		
			
				|  |  | -            0: [0; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        s
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn clear(&mut self) {
 | 
	
		
			
				|  |  | -        self.0 = [0; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn set(&mut self, x: u8, y: u8, value: u8) {
 | 
	
		
			
				|  |  | -        self.0[pos(x, y) as usize] = value;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn get(&mut self, x: u8, y: u8) -> u8 {
 | 
	
		
			
				|  |  | -        self.0[pos(x, y) as usize]
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Load puzzle from a string.
 | 
	
		
			
				|  |  | -    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
 | 
	
		
			
				|  |  | -    pub fn load_from_tld(&mut self, start_ch: char, blank: char, s: &str) {
 | 
	
		
			
				|  |  | -        self.clear();
 | 
	
		
			
				|  |  | -        let mut x: u8 = 0;
 | 
	
		
			
				|  |  | -        let mut y: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for ch in s.chars() {
 | 
	
		
			
				|  |  | -            if ch != blank {
 | 
	
		
			
				|  |  | -                self.set(x, y, (ch as u8 - start_ch as u8) + 1);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            y += 1;
 | 
	
		
			
				|  |  | -            if y >= WIDTH {
 | 
	
		
			
				|  |  | -                y = 0;
 | 
	
		
			
				|  |  | -                x += 1;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Save puzzle to a string.
 | 
	
		
			
				|  |  | -    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn save_to_tld(&mut self, start_ch: char, blank: char) -> String {
 | 
	
		
			
				|  |  | -        let mut result = String::new();
 | 
	
		
			
				|  |  | -        result.reserve(MAX_SIZE as usize);
 | 
	
		
			
				|  |  | -        let start_ch = (start_ch as u8 - 1) as char;
 | 
	
		
			
				|  |  | -        let mut x: u8 = 0;
 | 
	
		
			
				|  |  | -        let mut y: u8 = 0;
 | 
	
		
			
				|  |  | -        for _i in 0..MAX_SIZE {
 | 
	
		
			
				|  |  | -            if self.0[pos(x, y) as usize] == 0 {
 | 
	
		
			
				|  |  | -                result.push(blank);
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -                result.push((start_ch as u8 + self.0[pos(x, y) as usize]) as char);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            y += 1;
 | 
	
		
			
				|  |  | -            if y >= WIDTH {
 | 
	
		
			
				|  |  | -                y = 0;
 | 
	
		
			
				|  |  | -                x += 1;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        result
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  #[derive(Debug, Clone)]
 | 
	
		
			
				|  |  |  struct GameLoadError {
 | 
	
		
			
				|  |  |      message: String,
 | 
	
	
		
			
				|  | @@ -916,7 +835,6 @@ impl AnySolver {
 | 
	
		
			
				|  |  |                  // - Record that we did something.
 | 
	
		
			
				|  |  |                  pass1 = true;
 | 
	
		
			
				|  |  |                  pass1_again = true;
 | 
	
		
			
				|  |  | -                found_something = false;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          pass1
 | 
	
	
		
			
				|  | @@ -999,7 +917,7 @@ impl AnySolver {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              if found_something == true {
 | 
	
		
			
				|  |  |                  // Ok, pass 2 found something.
 | 
	
		
			
				|  |  | -                pass2 = true;                
 | 
	
		
			
				|  |  | +                pass2 = true;
 | 
	
		
			
				|  |  |                  found_something = false;
 | 
	
		
			
				|  |  |                  pass2_again = true;
 | 
	
		
			
				|  |  |                  // continue;
 | 
	
	
		
			
				|  | @@ -1009,344 +927,354 @@ impl AnySolver {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      /// Solve by logic alone.
 | 
	
		
			
				|  |  | -    /// - Returns true when something was added to board.
 | 
	
		
			
				|  |  | -    /// - Call solve until it returns false.
 | 
	
		
			
				|  |  | +    /// - Returns true if solved.
 | 
	
		
			
				|  |  |      /// - It might not be solved (if guessing is required).
 | 
	
		
			
				|  |  |      pub fn solve_logic(&mut self) -> bool {
 | 
	
		
			
				|  |  |          // self.reset_possible(); // destroys anything pass3 accomplishes ...
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // self.board.display();
 | 
	
		
			
				|  |  |          // self.possible.display();
 | 
	
		
			
				|  |  | -        let mut result = true;
 | 
	
		
			
				|  |  | -        // Pass 1: Look for singles in the possible sets.
 | 
	
		
			
				|  |  | -        while result {
 | 
	
		
			
				|  |  | -            result = self.logic_pass1();
 | 
	
		
			
				|  |  | -            println!("Pass1");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let mut result2 = true;
 | 
	
		
			
				|  |  | -        while result2 {
 | 
	
		
			
				|  |  | -            result2 = self.logic_pass2();
 | 
	
		
			
				|  |  | -            println!("Pass2");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        let mut found_more = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        result || result2
 | 
	
		
			
				|  |  | -        /*
 | 
	
		
			
				|  |  | -        // Pass 2: Is the same as Pass 1!
 | 
	
		
			
				|  |  | -        // We just look in groups, instead of all the indexes.
 | 
	
		
			
				|  |  | +        while found_more {
 | 
	
		
			
				|  |  | +            found_more = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let mut found_something = false;
 | 
	
		
			
				|  |  | -        let mut pass1 = false;
 | 
	
		
			
				|  |  | +            let mut result1 = true;
 | 
	
		
			
				|  |  | +            // Pass 1: Look for singles in the possible sets.
 | 
	
		
			
				|  |  | +            while result1 {
 | 
	
		
			
				|  |  | +                result1 = self.logic_pass1();
 | 
	
		
			
				|  |  | +                if result1 {
 | 
	
		
			
				|  |  | +                    println!("Pass1");
 | 
	
		
			
				|  |  | +                };
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // Repeat pass 1 until all have been found.
 | 
	
		
			
				|  |  | -        let mut pass1_again = true;
 | 
	
		
			
				|  |  | +            let mut result2 = true;
 | 
	
		
			
				|  |  | +            while result2 {
 | 
	
		
			
				|  |  | +                result2 = self.logic_pass2();
 | 
	
		
			
				|  |  | +                if result2 {
 | 
	
		
			
				|  |  | +                    println!("Pass2");
 | 
	
		
			
				|  |  | +                    found_more = true;
 | 
	
		
			
				|  |  | +                };
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let width = self.group.width;
 | 
	
		
			
				|  |  | -        let grp = self.group.clone();
 | 
	
		
			
				|  |  | -        let mut pass2 = false;
 | 
	
		
			
				|  |  | +            /*
 | 
	
		
			
				|  |  | +            // Pass 2: Is the same as Pass 1!
 | 
	
		
			
				|  |  | +            // We just look in groups, instead of all the indexes.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        while pass1_again {
 | 
	
		
			
				|  |  | -            // Pass 1 - look for singles.
 | 
	
		
			
				|  |  | +            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;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                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;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | +            let width = self.group.width;
 | 
	
		
			
				|  |  | +            let grp = self.group.clone();
 | 
	
		
			
				|  |  | +            let mut pass2 = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            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.
 | 
	
		
			
				|  |  | +            while pass1_again {
 | 
	
		
			
				|  |  | +                // Pass 1 - look for singles.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // Pass 2 - Look for singles within the groups.
 | 
	
		
			
				|  |  | +                for i in 0..self.possible.max_index {
 | 
	
		
			
				|  |  | +                    if self.board.board[i] != 0 {
 | 
	
		
			
				|  |  | +                        // Skip, if position already filled.
 | 
	
		
			
				|  |  | +                        continue;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // Are we done?
 | 
	
		
			
				|  |  | -                /*
 | 
	
		
			
				|  |  | -                if self.board.complete() {
 | 
	
		
			
				|  |  | -                    return false;
 | 
	
		
			
				|  |  | +                    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;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                // We're getting stuck in pass 2 ...
 | 
	
		
			
				|  |  | -                // not anymore.  ;)
 | 
	
		
			
				|  |  | -                println!("Pass 2:");
 | 
	
		
			
				|  |  | -                self.board.display();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                found_something = false;
 | 
	
		
			
				|  |  | +                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.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // let mut values = Bits(0); // HashSet<u8> = HashSet::new();
 | 
	
		
			
				|  |  | -                let mut values = GenBits::<u32>(0);
 | 
	
		
			
				|  |  | +                    // Pass 2 - Look for singles within the groups.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                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 as usize, true);
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        // values.extend(this.possible[grp.0[gidx as usize] as usize]);
 | 
	
		
			
				|  |  | -                        // println!("now     : {:?}", this.possible[grp.items[gidx as usize] as usize]);
 | 
	
		
			
				|  |  | +                    // Are we done?
 | 
	
		
			
				|  |  | +                    /*
 | 
	
		
			
				|  |  | +                    if self.board.complete() {
 | 
	
		
			
				|  |  | +                        return false;
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | +                    // We're getting stuck in pass 2 ...
 | 
	
		
			
				|  |  | +                    // not anymore.  ;)
 | 
	
		
			
				|  |  | +                    println!("Pass 2:");
 | 
	
		
			
				|  |  | +                    self.board.display();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    // println!("values {:?}", values);
 | 
	
		
			
				|  |  | +                    */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    // Now, check for singles.
 | 
	
		
			
				|  |  | -                    for v in values.iter() {
 | 
	
		
			
				|  |  | -                        // println!("Check Value: {}", v);
 | 
	
		
			
				|  |  | +                    found_something = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                        let mut count = 0;
 | 
	
		
			
				|  |  | -                        let mut pos = 0;
 | 
	
		
			
				|  |  | +                    // let mut values = Bits(0); // HashSet<u8> = HashSet::new();
 | 
	
		
			
				|  |  | +                    let mut values = GenBits::<u32>(0);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +                    let mut group_process = |this: &mut Self, grp: &[usize]| {
 | 
	
		
			
				|  |  | +                        // Collect all the possible values within the group.
 | 
	
		
			
				|  |  | +                        values.clear();
 | 
	
		
			
				|  |  |                          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);
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | +                            // 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 as usize, true);
 | 
	
		
			
				|  |  |                              }
 | 
	
		
			
				|  |  | +                            // values.extend(this.possible[grp.0[gidx as usize] as usize]);
 | 
	
		
			
				|  |  | +                            // println!("now     : {:?}", this.possible[grp.items[gidx as usize] as usize]);
 | 
	
		
			
				|  |  |                          }
 | 
	
		
			
				|  |  | -                        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;
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    // Ok, we didn't find anything.
 | 
	
		
			
				|  |  | -                    // Break out of loop, get unstuck.
 | 
	
		
			
				|  |  | -                    break;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // Pass 3:
 | 
	
		
			
				|  |  | -        // - Find pairs & remove those numbers from corresponding group.
 | 
	
		
			
				|  |  | +                        // println!("values {:?}", values);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let grp = self.group.clone();
 | 
	
		
			
				|  |  | +                        // Now, check for singles.
 | 
	
		
			
				|  |  | +                        for v in values.iter() {
 | 
	
		
			
				|  |  | +                            // println!("Check Value: {}", v);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        println!("Pass 3:");
 | 
	
		
			
				|  |  | -        self.board.display();
 | 
	
		
			
				|  |  | -        self.possible.display();
 | 
	
		
			
				|  |  | +                            let mut count = 0;
 | 
	
		
			
				|  |  | +                            let mut pos = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        assert_eq!(self.board.width, self.possible.width);
 | 
	
		
			
				|  |  | +                            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);
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // Pair processing.
 | 
	
		
			
				|  |  | -        for i in 0..width {
 | 
	
		
			
				|  |  | -            let mut g = grp.cell(i);
 | 
	
		
			
				|  |  | -            println!("Cell {}: {:?}", i, g);
 | 
	
		
			
				|  |  | +                    if found_something == true {
 | 
	
		
			
				|  |  | +                        pass2 = true;
 | 
	
		
			
				|  |  | +                        // Ok, pass 2 found something.
 | 
	
		
			
				|  |  | +                        found_something = false;
 | 
	
		
			
				|  |  | +                        continue;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            for gidx in 0..WIDTH - 1 {
 | 
	
		
			
				|  |  | -                let gpos = g[gidx as usize];
 | 
	
		
			
				|  |  | -                if self.possible.possible[gpos].count_set() == 2 {
 | 
	
		
			
				|  |  | -                    // Found a pair
 | 
	
		
			
				|  |  | +                    //
 | 
	
		
			
				|  |  | +                    // - 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;
 | 
	
		
			
				|  |  | +                    } else {
 | 
	
		
			
				|  |  | +                        // Ok, we didn't find anything.
 | 
	
		
			
				|  |  | +                        // Break out of loop, get unstuck.
 | 
	
		
			
				|  |  | +                        break;
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    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])
 | 
	
		
			
				|  |  | +            // Pass 3:
 | 
	
		
			
				|  |  | +            // - Find pairs & remove those numbers from corresponding group.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                            if self.possible.possible[gpos] == self.possible.possible[fpos] {
 | 
	
		
			
				|  |  | -                                // 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 grp = self.group.clone();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                // This looks .. WRONG!  only z+1 when using set(value)!
 | 
	
		
			
				|  |  | +            println!("Pass 3:");
 | 
	
		
			
				|  |  | +            self.board.display();
 | 
	
		
			
				|  |  | +            self.possible.display();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                let mut values: [u8; 2] = [0, 0];
 | 
	
		
			
				|  |  | -                                let mut vpos = 0;
 | 
	
		
			
				|  |  | -                                for z in self.possible.possible[gpos].iter() {
 | 
	
		
			
				|  |  | -                                    values[vpos] = z;
 | 
	
		
			
				|  |  | -                                    vpos += 1;
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | +            assert_eq!(self.board.width, self.possible.width);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                // Ok, other then being off by 1 (because x,y starts at 0 not 1),
 | 
	
		
			
				|  |  | -                                // This is, at least, displaying the information properly.
 | 
	
		
			
				|  |  | +            // Pair processing.
 | 
	
		
			
				|  |  | +            for i in 0..width {
 | 
	
		
			
				|  |  | +                let mut g = grp.cell(i);
 | 
	
		
			
				|  |  | +                println!("Cell {}: {:?}", i, g);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                self.possible.display();
 | 
	
		
			
				|  |  | -                                println!(
 | 
	
		
			
				|  |  | -                                    "Pairs {gpos}({},{}) and {fpos}({},{}) {:?}",
 | 
	
		
			
				|  |  | -                                    self.possible.xy(gpos).0 + 1,
 | 
	
		
			
				|  |  | -                                    self.possible.xy(gpos).1 + 1,
 | 
	
		
			
				|  |  | -                                    self.possible.xy(fpos).0 + 1,
 | 
	
		
			
				|  |  | -                                    self.possible.xy(fpos).1 + 1,
 | 
	
		
			
				|  |  | -                                    values
 | 
	
		
			
				|  |  | -                                );
 | 
	
		
			
				|  |  | +                for gidx in 0..WIDTH - 1 {
 | 
	
		
			
				|  |  | +                    let gpos = g[gidx as usize];
 | 
	
		
			
				|  |  | +                    if self.possible.possible[gpos].count_set() == 2 {
 | 
	
		
			
				|  |  | +                        // Found a pair
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                let mut pair_removed = false;
 | 
	
		
			
				|  |  | +                        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])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                // Check to see if anything was removed.
 | 
	
		
			
				|  |  | +                                if self.possible.possible[gpos] == self.possible.possible[fpos] {
 | 
	
		
			
				|  |  | +                                    // 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!
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                for remove in 0..width {
 | 
	
		
			
				|  |  | -                                    if (gidx == remove) || (fidx == remove) {
 | 
	
		
			
				|  |  | -                                        // Skip the found pair indexes.  Don't remove those!
 | 
	
		
			
				|  |  | -                                        continue;
 | 
	
		
			
				|  |  | -                                    }
 | 
	
		
			
				|  |  | +                                    // This looks .. WRONG!  only z+1 when using set(value)!
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                    // 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;
 | 
	
		
			
				|  |  | -                                        println!("Removed {} from {}({},{})", values[0], rpos, self.possible.xy(rpos).0, self.possible.xy(rpos).1);
 | 
	
		
			
				|  |  | +                                    let mut values: [u8; 2] = [0, 0];
 | 
	
		
			
				|  |  | +                                    let mut vpos = 0;
 | 
	
		
			
				|  |  | +                                    for z in self.possible.possible[gpos].iter() {
 | 
	
		
			
				|  |  | +                                        values[vpos] = z;
 | 
	
		
			
				|  |  | +                                        vpos += 1;
 | 
	
		
			
				|  |  |                                      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                    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;
 | 
	
		
			
				|  |  | -                                        println!("Removed {} from {}({},{})", values[1], rpos, self.possible.xy(rpos).0, self.possible.xy(rpos).1);
 | 
	
		
			
				|  |  | -                                    }
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | +                                    // Ok, other then being off by 1 (because x,y starts at 0 not 1),
 | 
	
		
			
				|  |  | +                                    // This is, at least, displaying the information properly.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                if pair_removed {
 | 
	
		
			
				|  |  | -                                    println!("pair removed...");
 | 
	
		
			
				|  |  | +                                    self.possible.display();
 | 
	
		
			
				|  |  |                                      println!(
 | 
	
		
			
				|  |  | -                                        "--> Pairs {gpos}({},{}) and {fpos}({},{}) {:?}",
 | 
	
		
			
				|  |  | +                                        "Pairs {gpos}({},{}) and {fpos}({},{}) {:?}",
 | 
	
		
			
				|  |  |                                          self.possible.xy(gpos).0 + 1,
 | 
	
		
			
				|  |  |                                          self.possible.xy(gpos).1 + 1,
 | 
	
		
			
				|  |  |                                          self.possible.xy(fpos).0 + 1,
 | 
	
		
			
				|  |  |                                          self.possible.xy(fpos).1 + 1,
 | 
	
		
			
				|  |  |                                          values
 | 
	
		
			
				|  |  |                                      );
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                // 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;
 | 
	
		
			
				|  |  | +                                    let mut pair_removed = false;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                    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) {
 | 
	
		
			
				|  |  | +                                    // Check to see if anything was removed.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                                    for remove in 0..width {
 | 
	
		
			
				|  |  | +                                        if (gidx == remove) || (fidx == remove) {
 | 
	
		
			
				|  |  | +                                            // Skip the found pair indexes.  Don't remove those!
 | 
	
		
			
				|  |  |                                              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);
 | 
	
		
			
				|  |  | +                                        // 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;
 | 
	
		
			
				|  |  | +                                            println!("Removed {} from {}({},{})", values[0], rpos, self.possible.xy(rpos).0, self.possible.xy(rpos).1);
 | 
	
		
			
				|  |  |                                          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                                        if self.possible.possible[pos].get(values[1] as usize) {
 | 
	
		
			
				|  |  | -                                            self.possible.possible[pos]
 | 
	
		
			
				|  |  | -                                                .set(values[1] as usize, false);
 | 
	
		
			
				|  |  | +                                        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;
 | 
	
		
			
				|  |  | +                                            println!("Removed {} from {}({},{})", values[1], rpos, self.possible.xy(rpos).0, self.possible.xy(rpos).1);
 | 
	
		
			
				|  |  |                                          }
 | 
	
		
			
				|  |  |                                      }
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                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;
 | 
	
		
			
				|  |  | +                                    if pair_removed {
 | 
	
		
			
				|  |  | +                                        println!("pair removed...");
 | 
	
		
			
				|  |  | +                                        println!(
 | 
	
		
			
				|  |  | +                                            "--> Pairs {gpos}({},{}) and {fpos}({},{}) {:?}",
 | 
	
		
			
				|  |  | +                                            self.possible.xy(gpos).0 + 1,
 | 
	
		
			
				|  |  | +                                            self.possible.xy(gpos).1 + 1,
 | 
	
		
			
				|  |  | +                                            self.possible.xy(fpos).0 + 1,
 | 
	
		
			
				|  |  | +                                            self.possible.xy(fpos).1 + 1,
 | 
	
		
			
				|  |  | +                                            values
 | 
	
		
			
				|  |  | +                                        );
 | 
	
		
			
				|  |  |                                      }
 | 
	
		
			
				|  |  | -                                    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;
 | 
	
		
			
				|  |  | +                                    // 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.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 true {
 | 
	
		
			
				|  |  | -                                        println!(
 | 
	
		
			
				|  |  | -                                            "Pair found! {} {}: {} {:?} and {} {:?} !",
 | 
	
		
			
				|  |  | -                                            gidx,
 | 
	
		
			
				|  |  | -                                            fidx,
 | 
	
		
			
				|  |  | -                                            gpos,
 | 
	
		
			
				|  |  | -                                            self.board.xy(gpos),
 | 
	
		
			
				|  |  | -                                            fpos,
 | 
	
		
			
				|  |  | -                                            self.board.xy(fpos)
 | 
	
		
			
				|  |  | -                                        );
 | 
	
		
			
				|  |  | -                                        self.possible.display();
 | 
	
		
			
				|  |  | -                                        // panic!("... We're lost ...");
 | 
	
		
			
				|  |  | +                                    if pair_removed {
 | 
	
		
			
				|  |  | +                                        if true {
 | 
	
		
			
				|  |  | +                                            println!(
 | 
	
		
			
				|  |  | +                                                "Pair found! {} {}: {} {:?} and {} {:?} !",
 | 
	
		
			
				|  |  | +                                                gidx,
 | 
	
		
			
				|  |  | +                                                fidx,
 | 
	
		
			
				|  |  | +                                                gpos,
 | 
	
		
			
				|  |  | +                                                self.board.xy(gpos),
 | 
	
		
			
				|  |  | +                                                fpos,
 | 
	
		
			
				|  |  | +                                                self.board.xy(fpos)
 | 
	
		
			
				|  |  | +                                            );
 | 
	
		
			
				|  |  | +                                            self.possible.display();
 | 
	
		
			
				|  |  | +                                            // panic!("... We're lost ...");
 | 
	
		
			
				|  |  | +                                        }
 | 
	
		
			
				|  |  |                                      }
 | 
	
		
			
				|  |  |                                  }
 | 
	
		
			
				|  |  |                              }
 | 
	
	
		
			
				|  | @@ -1354,10 +1282,12 @@ impl AnySolver {
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +            println!("Pass 3 - Ending...");
 | 
	
		
			
				|  |  | +            found_something
 | 
	
		
			
				|  |  | +            */
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        println!("Pass 3 - Ending...");
 | 
	
		
			
				|  |  | -        found_something
 | 
	
		
			
				|  |  | -        */
 | 
	
		
			
				|  |  | +        self.board.complete()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1396,9 +1326,8 @@ mod tests {
 | 
	
		
			
				|  |  |          assert!(solver.validate_board());
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          solver.reset_possible();
 | 
	
		
			
				|  |  | -        // solver.solve_logic();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        while (solver.solve_logic()) {
 | 
	
		
			
				|  |  | +        if solver.solve_logic() {
 | 
	
		
			
				|  |  |              solver.board.display();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1436,7 +1365,7 @@ mod tests {
 | 
	
		
			
				|  |  |          assert!(solver.validate_board());
 | 
	
		
			
				|  |  |          solver.board.display();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        while (solver.solve_logic()) {
 | 
	
		
			
				|  |  | +        if solver.solve_logic() {
 | 
	
		
			
				|  |  |              solver.board.display();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1546,17 +1475,6 @@ mod tests {
 | 
	
		
			
				|  |  |              let solutions = solver.board.brute_force_solver(2);
 | 
	
		
			
				|  |  |              assert!(solutions.0 == 1);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /*
 | 
	
		
			
				|  |  | -        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_");
 | 
	
		
			
				|  |  | -        assert!(result.is_ok());
 | 
	
		
			
				|  |  | -        board.display();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let mut solver = AnySolver::new(&board);
 | 
	
		
			
				|  |  | -        assert!(solver.validate_board());
 | 
	
		
			
				|  |  | -        assert!(solver.brute_force_solver() == 1);
 | 
	
		
			
				|  |  | -        */
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      #[test]
 | 
	
	
		
			
				|  | @@ -1632,861 +1550,50 @@ mod tests {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -I probably should keep board and possible separate from one another.
 | 
	
		
			
				|  |  | -Possible is only used when solving the puzzles, and only used by the
 | 
	
		
			
				|  |  | -logic solver.  Not needed by brute-force.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -Ok, the problem is that the possible doesn't make any sense to be
 | 
	
		
			
				|  |  | -unlinked in any way from the board.  I think they have to be together.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -Right now, I have &mut of board, it might be better to just copy it.
 | 
	
		
			
				|  |  | -Then, I could derive Clone and Copy again for BoardPossible...
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -(And simplify things immensely, getting rid of lifetimes...)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -Still thinking of having Possible as a structure, so I could implement
 | 
	
		
			
				|  |  | -funcs on just that, rather then having them in BoardPossible (wrong
 | 
	
		
			
				|  |  | -place for Possible's implementation).
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -#[derive(Debug, Clone, Copy)]
 | 
	
		
			
				|  |  | -pub struct SudokuPossible {
 | 
	
		
			
				|  |  | -    pub possible: [Possible; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // 10 = WIDTH + 1
 | 
	
		
			
				|  |  | -        // This would need to be changed in order to handle 4x4 or 5x5.
 | 
	
		
			
				|  |  | -        // NOTE: We ignore bit 0, we use bits 1-9 (for 3x3).
 | 
	
		
			
				|  |  | -        let mut initial: Bits = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let s = Sudoku {
 | 
	
		
			
				|  |  | -            board: [0; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -            possible: [initial; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -*/
 | 
	
		
			
				|  |  | +/* 
 | 
	
		
			
				|  |  | +    /// Puzzle generation
 | 
	
		
			
				|  |  | +    /// - Removes a number, tests to see if the puzzle can still be solved by logic.
 | 
	
		
			
				|  |  | +    /// - Returns true when still a valid, solvable puzzle.
 | 
	
		
			
				|  |  | +    /// - Otherwise, restores number and returns false.
 | 
	
		
			
				|  |  | +    pub fn remove(&mut self) -> bool {
 | 
	
		
			
				|  |  | +        // Find a number, remove it. Save position.
 | 
	
		
			
				|  |  | +        let mut rng = ChaCha20Rng::from_entropy();
 | 
	
		
			
				|  |  | +        let puzrange = Uniform::new(0, WIDTH);
 | 
	
		
			
				|  |  | +        let mut x = 0;
 | 
	
		
			
				|  |  | +        let mut y = 0;
 | 
	
		
			
				|  |  | +        let mut value: u8 = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -#[derive(Debug)]
 | 
	
		
			
				|  |  | -pub struct BoardPossible<'a> {
 | 
	
		
			
				|  |  | -    board: &'a mut Board,
 | 
	
		
			
				|  |  | -    possible: [Bits; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +        while value == 0 {
 | 
	
		
			
				|  |  | +            x = puzrange.sample(&mut rng);
 | 
	
		
			
				|  |  | +            y = puzrange.sample(&mut rng);
 | 
	
		
			
				|  |  | +            value = self.get(x, y);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -impl<'a> BoardPossible<'a> {
 | 
	
		
			
				|  |  | -    pub fn new(board: &'a mut Board) -> Self {
 | 
	
		
			
				|  |  | -        // 10 = WIDTH + 1
 | 
	
		
			
				|  |  | -        // This would need to be changed in order to handle 4x4 or 5x5.
 | 
	
		
			
				|  |  | -        // NOTE: We ignore bit 0, we use bits 1-9 (for 3x3).
 | 
	
		
			
				|  |  | -        let mut initial: Bits = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | +        self.set(x, y, 0);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        let mut s = Self {
 | 
	
		
			
				|  |  | -            board: board,
 | 
	
		
			
				|  |  | -            possible: [initial; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        s.reset_possible();
 | 
	
		
			
				|  |  | -        s
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +        // Clone, and solve by logic.
 | 
	
		
			
				|  |  | +        let mut puzcopy = self.clone();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    pub fn clear_possible(&mut self) {
 | 
	
		
			
				|  |  | -        let mut initial: Bits = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | -        self.possible = [initial; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | +        puzcopy.reset_possible();
 | 
	
		
			
				|  |  | +        /*
 | 
	
		
			
				|  |  | +        puzcopy.display();
 | 
	
		
			
				|  |  | +        puzcopy.display_possible();
 | 
	
		
			
				|  |  | +         */
 | 
	
		
			
				|  |  | +        // If solvable, return true.
 | 
	
		
			
				|  |  | +        while puzcopy.solve(false) {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// set (x,y) with value.
 | 
	
		
			
				|  |  | -    /// - This updates the board.
 | 
	
		
			
				|  |  | -    /// - this updates all the possible (row,column,cell).
 | 
	
		
			
				|  |  | -    /// - Clears the possible for the (x,y).
 | 
	
		
			
				|  |  | -    pub fn set(&mut self, x: u8, y: u8, value: u8) {
 | 
	
		
			
				|  |  | -        self.board.0[pos(x, y)] = value;
 | 
	
		
			
				|  |  | +        /*
 | 
	
		
			
				|  |  | +        puzcopy.display();
 | 
	
		
			
				|  |  | +        puzcopy.display_possible();
 | 
	
		
			
				|  |  | +        */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        // update the possible
 | 
	
		
			
				|  |  | -        let mut g: &Group = for_row(y);
 | 
	
		
			
				|  |  | -        for g in g.0 {
 | 
	
		
			
				|  |  | -            self.possible[g].set(value, false);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        g = for_column(x);
 | 
	
		
			
				|  |  | -        for g in g.0 {
 | 
	
		
			
				|  |  | -            self.possible[g].set(value, false);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        g = for_cell(which_cell(x, y));
 | 
	
		
			
				|  |  | -        for g in g.0 {
 | 
	
		
			
				|  |  | -            self.possible[g].set(value, false);
 | 
	
		
			
				|  |  | +        if puzcopy.puzzle_complete() {
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | -        self.possible[pos(x, y)].clear();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn reset_possible(&mut self) {}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Display the possibles.
 | 
	
		
			
				|  |  | -    /// This should be in the Possible struct, not here.
 | 
	
		
			
				|  |  | -    pub fn display(&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() {
 | 
	
		
			
				|  |  | -                    // print!("{},", p);
 | 
	
		
			
				|  |  | -                    possible += format!("{},", p).as_str();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                // for i in 0..SIZE {
 | 
	
		
			
				|  |  | -                // let &pos = self.possible[pos(x, y) as usize];
 | 
	
		
			
				|  |  | -                print!("({},{}):", x, y);
 | 
	
		
			
				|  |  | -                // print!("{:20}", format!("{:?}", self.possible[pos(x, y) as usize]));
 | 
	
		
			
				|  |  | -                print!("{:9}", possible);
 | 
	
		
			
				|  |  | -                /*
 | 
	
		
			
				|  |  | -                if pos == 0 {
 | 
	
		
			
				|  |  | -                    print!(" ");
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    print!("{}", pos);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                */
 | 
	
		
			
				|  |  | -                // }
 | 
	
		
			
				|  |  | -                // print!(" ");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            println!("");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        // If not solvable, restore number, return false.
 | 
	
		
			
				|  |  | +        self.set(x, y, value);
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  |  */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -  ___  _     _    ____          _
 | 
	
		
			
				|  |  | - / _ \| | __| |  / ___|___   __| | ___
 | 
	
		
			
				|  |  | -| | | | |/ _` | | |   / _ \ / _` |/ _ \
 | 
	
		
			
				|  |  | -| |_| | | (_| | | |__| (_) | (_| |  __/
 | 
	
		
			
				|  |  | - \___/|_|\__,_|  \____\___/ \__,_|\___|
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | - */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#[derive(Debug, Clone, Copy)]
 | 
	
		
			
				|  |  | -pub struct Sudoku {
 | 
	
		
			
				|  |  | -    pub board: [u8; MAX_SIZE as usize],      // Board
 | 
	
		
			
				|  |  | -    pub possible: [Bits; MAX_SIZE as usize], // BoardPossible
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/// Translate x,y to position in board.
 | 
	
		
			
				|  |  | -/// This is used as usize, so return that instead of u8.
 | 
	
		
			
				|  |  | -pub const fn pos(x: u8, y: u8) -> usize {
 | 
	
		
			
				|  |  | -    x as usize + (y as usize * WIDTH as usize)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/// Translate x,y (with starting index of 1) to position in board.
 | 
	
		
			
				|  |  | -pub const fn pos1(x: u8, y: u8) -> usize {
 | 
	
		
			
				|  |  | -    (x as usize - 1) + ((y as usize - 1) * WIDTH as usize)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/// Translate post to x,y in board.
 | 
	
		
			
				|  |  | -pub const fn xy(pos: usize) -> (u8, u8) {
 | 
	
		
			
				|  |  | -    ((pos % WIDTH as usize) as u8, (pos / WIDTH as usize) as u8)
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -const DEBUG_OUTPUT: bool = false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/*
 | 
	
		
			
				|  |  | -(0 .. 10)
 | 
	
		
			
				|  |  | -                        .map(|_| HashSet::<usize>::new())
 | 
	
		
			
				|  |  | -                        .collect();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -let arr: [Vec<u32>; 10] = [(); 10].map(|_| Vec::with_capacity(100));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -*/
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -impl Sudoku {
 | 
	
		
			
				|  |  | -    pub fn new() -> Self {
 | 
	
		
			
				|  |  | -        // 10 = WIDTH + 1
 | 
	
		
			
				|  |  | -        // This would need to be changed in order to handle 4x4 or 5x5.
 | 
	
		
			
				|  |  | -        // NOTE: We ignore bit 0, we use bits 1-9 (for 3x3).
 | 
	
		
			
				|  |  | -        let mut initial: Bits = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let s = Sudoku {
 | 
	
		
			
				|  |  | -            board: [0; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -            possible: [initial; MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -        s
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn clear_possible(&mut self) {
 | 
	
		
			
				|  |  | -        let mut initial = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | -        self.possible = [initial; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn clear(&mut self) {
 | 
	
		
			
				|  |  | -        let mut initial = Bits(set_bits(10));
 | 
	
		
			
				|  |  | -        initial.set(0, false);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        self.board = [0; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -        self.possible = [initial; MAX_SIZE as usize];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Load puzzle from a string.
 | 
	
		
			
				|  |  | -    /// Note, we load from (top,left) going down, to (bottom,left) by columns.
 | 
	
		
			
				|  |  | -    pub fn load_from_tld(&mut self, start_ch: char, blank: char, s: &str) {
 | 
	
		
			
				|  |  | -        self.clear();
 | 
	
		
			
				|  |  | -        let mut x: u8 = 0;
 | 
	
		
			
				|  |  | -        let mut y: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for ch in s.chars() {
 | 
	
		
			
				|  |  | -            if ch != blank {
 | 
	
		
			
				|  |  | -                self.set(x, y, (ch as u8 - start_ch as u8) + 1);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            y += 1;
 | 
	
		
			
				|  |  | -            if y >= WIDTH {
 | 
	
		
			
				|  |  | -                y = 0;
 | 
	
		
			
				|  |  | -                x += 1;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Load puzzle from a string.
 | 
	
		
			
				|  |  | -    /// This loads from (top,left) going right, to (top,right), by rows.
 | 
	
		
			
				|  |  | -    pub fn load_from_tlr(&mut self, start_ch: char, blank: char, s: &str) {
 | 
	
		
			
				|  |  | -        self.clear();
 | 
	
		
			
				|  |  | -        let mut i: usize = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for ch in s.chars() {
 | 
	
		
			
				|  |  | -            if ch != blank {
 | 
	
		
			
				|  |  | -                self.set(xy(i).0, xy(i).1, (ch as u8 - start_ch as u8) + 1);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            i += 1;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn save_to_tld(&self, mut start_ch: char, blank: char) -> String {
 | 
	
		
			
				|  |  | -        let mut result = String::new();
 | 
	
		
			
				|  |  | -        result.reserve(MAX_SIZE as usize);
 | 
	
		
			
				|  |  | -        start_ch = (start_ch as u8 - 1) as char;
 | 
	
		
			
				|  |  | -        let mut x: u8 = 0;
 | 
	
		
			
				|  |  | -        let mut y: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for _i in 0..MAX_SIZE {
 | 
	
		
			
				|  |  | -            if self.board[pos(x, y) as usize] == 0 {
 | 
	
		
			
				|  |  | -                result.push(blank);
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -                result.push((start_ch as u8 + self.board[pos(x, y) as usize]) as char);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            y += 1;
 | 
	
		
			
				|  |  | -            if y >= WIDTH {
 | 
	
		
			
				|  |  | -                y = 0;
 | 
	
		
			
				|  |  | -                x += 1;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        result
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn save_to_tlr(&self, mut start_ch: char, blank: char) -> String {
 | 
	
		
			
				|  |  | -        let mut result = String::new();
 | 
	
		
			
				|  |  | -        result.reserve(MAX_SIZE as usize);
 | 
	
		
			
				|  |  | -        start_ch = (start_ch as u8 - 1) as char;
 | 
	
		
			
				|  |  | -        for i in 0..MAX_SIZE {
 | 
	
		
			
				|  |  | -            if self.board[i as usize] == 0 {
 | 
	
		
			
				|  |  | -                result.push(blank);
 | 
	
		
			
				|  |  | -            } else {
 | 
	
		
			
				|  |  | -                result.push((start_ch as u8 + self.board[i as usize]) as char);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        result
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn get(&self, x: u8, y: u8) -> u8 {
 | 
	
		
			
				|  |  | -        self.board[pos(x, y) as usize]
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /*
 | 
	
		
			
				|  |  | -    This set does more then it needs to.
 | 
	
		
			
				|  |  | -    When setting a location, it also updates the possible.
 | 
	
		
			
				|  |  | -    This needs to be moved into something else.  Putting it into Possible?
 | 
	
		
			
				|  |  | -     */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    pub fn set(&mut self, x: u8, y: u8, value: u8) {
 | 
	
		
			
				|  |  | -        self.board[pos(x, y) as usize] = value;
 | 
	
		
			
				|  |  | -        // Ok, update the possible
 | 
	
		
			
				|  |  | -        let mut g = for_row(y);
 | 
	
		
			
				|  |  | -        // g.for_row(x, y);
 | 
	
		
			
				|  |  | -        for g in g.0 {
 | 
	
		
			
				|  |  | -            // remove value from these sets.
 | 
	
		
			
				|  |  | -            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].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].set(value, false);
 | 
	
		
			
				|  |  | -            // self.possible[g as usize].take(&value);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        self.possible[pos(x, y) as usize].clear();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Reset the Possible
 | 
	
		
			
				|  |  | -    /// - For when a new puzzle has been loaded.
 | 
	
		
			
				|  |  | -    /// - When something has changed, and the possibles are out of sync.
 | 
	
		
			
				|  |  | -    pub fn reset_possible(&mut self) {
 | 
	
		
			
				|  |  | -        // Reset the possible.
 | 
	
		
			
				|  |  | -        self.clear_possible();
 | 
	
		
			
				|  |  | -        for y in 0..WIDTH {
 | 
	
		
			
				|  |  | -            for x in 0..WIDTH {
 | 
	
		
			
				|  |  | -                let item: u8 = self.board[pos(x as u8, y as u8) as usize];
 | 
	
		
			
				|  |  | -                if item != 0 {
 | 
	
		
			
				|  |  | -                    let mut g: &Group = for_row(y);
 | 
	
		
			
				|  |  | -                    for g in g.0 {
 | 
	
		
			
				|  |  | -                        self.possible[g as usize].set(item, false);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    g = for_column(x);
 | 
	
		
			
				|  |  | -                    for g in g.0 {
 | 
	
		
			
				|  |  | -                        self.possible[g as usize].set(item, false);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    g = for_cell(which_cell(x, y));
 | 
	
		
			
				|  |  | -                    for g in g.0 {
 | 
	
		
			
				|  |  | -                        self.possible[g as usize].set(item, false);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    self.possible[pos(x, y) as usize].clear();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Display the sudoku puzzle.
 | 
	
		
			
				|  |  | -    pub fn display(&self) {
 | 
	
		
			
				|  |  | -        println!("╔═══╦═══╦═══╗");
 | 
	
		
			
				|  |  | -        for y in 0..WIDTH {
 | 
	
		
			
				|  |  | -            print!("║");
 | 
	
		
			
				|  |  | -            for x in 0..WIDTH {
 | 
	
		
			
				|  |  | -                let item = self.board[pos(x as u8, y as u8) as usize];
 | 
	
		
			
				|  |  | -                if item == 0 {
 | 
	
		
			
				|  |  | -                    print!(" ");
 | 
	
		
			
				|  |  | -                } else if (item >= 1) && (item <= 9) {
 | 
	
		
			
				|  |  | -                    print!("{}", item);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                if x % 3 == 2 {
 | 
	
		
			
				|  |  | -                    print!("║");
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            println!("");
 | 
	
		
			
				|  |  | -            if y % 3 == 2 {
 | 
	
		
			
				|  |  | -                if y + 1 == WIDTH {
 | 
	
		
			
				|  |  | -                    println!("╚═══╩═══╩═══╝");
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    println!("╠═══╬═══╬═══╣");
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Display the possibles.
 | 
	
		
			
				|  |  | -    /// This should be in the Possible struct, not here.
 | 
	
		
			
				|  |  | -    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() {
 | 
	
		
			
				|  |  | -                    // print!("{},", p);
 | 
	
		
			
				|  |  | -                    possible += format!("{},", p).as_str();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // for i in 0..SIZE {
 | 
	
		
			
				|  |  | -                // let &pos = self.possible[pos(x, y) as usize];
 | 
	
		
			
				|  |  | -                print!("({},{}):", x, y);
 | 
	
		
			
				|  |  | -                // print!("{:20}", format!("{:?}", self.possible[pos(x, y) as usize]));
 | 
	
		
			
				|  |  | -                print!("{:9}", possible);
 | 
	
		
			
				|  |  | -                /*
 | 
	
		
			
				|  |  | -                if pos == 0 {
 | 
	
		
			
				|  |  | -                    print!(" ");
 | 
	
		
			
				|  |  | -                } else {
 | 
	
		
			
				|  |  | -                    print!("{}", pos);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                */
 | 
	
		
			
				|  |  | -                // }
 | 
	
		
			
				|  |  | -                // print!(" ");
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            println!("");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Is the puzzle complete?
 | 
	
		
			
				|  |  | -    /// Have all of the locations been filled with a value?
 | 
	
		
			
				|  |  | -    /// - This does not validate that it is a correct puzzle,
 | 
	
		
			
				|  |  | -    ///   - It doesn't check for duplicate digits (for example).
 | 
	
		
			
				|  |  | -    pub fn puzzle_complete(&self) -> bool {
 | 
	
		
			
				|  |  | -        for i in 0..MAX_SIZE {
 | 
	
		
			
				|  |  | -            if self.board[i as usize] == 0 {
 | 
	
		
			
				|  |  | -                return false;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        true
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Recursive brute-force solver
 | 
	
		
			
				|  |  | -    /// - As possibilities are tried, it recursively calls itself to see
 | 
	
		
			
				|  |  | -    ///   if there are any solutions with the given possibility.
 | 
	
		
			
				|  |  | -    fn calculate_possible(
 | 
	
		
			
				|  |  | -        &mut self,
 | 
	
		
			
				|  |  | -        total_solutions: &mut u16,
 | 
	
		
			
				|  |  | -        solutions: &mut Vec<SudokuBoard>,
 | 
	
		
			
				|  |  | -    ) -> bool {
 | 
	
		
			
				|  |  | -        for idx in 0..MAX_SIZE as usize {
 | 
	
		
			
				|  |  | -            if self.board[idx as usize] == 0 {
 | 
	
		
			
				|  |  | -                // Ok, there's a blank here
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                let (x, y) = xy(idx);
 | 
	
		
			
				|  |  | -                if DEBUG_OUTPUT {
 | 
	
		
			
				|  |  | -                    println!("idx={} ({},{})", idx, x, y);
 | 
	
		
			
				|  |  | -                    self.display();
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                'outer: for possible in 1..=9 {
 | 
	
		
			
				|  |  | -                    let mut g = for_row(y);
 | 
	
		
			
				|  |  | -                    for p in g.0 {
 | 
	
		
			
				|  |  | -                        if self.board[p as usize] == possible {
 | 
	
		
			
				|  |  | -                            continue 'outer;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    g = for_column(x);
 | 
	
		
			
				|  |  | -                    for p in g.0 {
 | 
	
		
			
				|  |  | -                        if self.board[p as usize] == possible {
 | 
	
		
			
				|  |  | -                            continue 'outer;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    // check cell
 | 
	
		
			
				|  |  | -                    let cell = which_cell(x, y);
 | 
	
		
			
				|  |  | -                    g = for_cell(cell);
 | 
	
		
			
				|  |  | -                    for p in g.0 {
 | 
	
		
			
				|  |  | -                        if self.board[p as usize] == possible {
 | 
	
		
			
				|  |  | -                            continue 'outer;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    // Ok, it could go here!
 | 
	
		
			
				|  |  | -                    if DEBUG_OUTPUT {
 | 
	
		
			
				|  |  | -                        println!("({},{})={}", x, y, possible);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                    self.board[idx as usize] = possible;
 | 
	
		
			
				|  |  | -                    if self.puzzle_complete() {
 | 
	
		
			
				|  |  | -                        if *total_solutions < solutions.capacity() as u16 {
 | 
	
		
			
				|  |  | -                            solutions.push(self.board);
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        *total_solutions += 1;
 | 
	
		
			
				|  |  | -                        /*
 | 
	
		
			
				|  |  | -                        println!("**SOLUTION**");
 | 
	
		
			
				|  |  | -                        self.display();
 | 
	
		
			
				|  |  | -                        println!("***");
 | 
	
		
			
				|  |  | -                         */
 | 
	
		
			
				|  |  | -                        break;
 | 
	
		
			
				|  |  | -                    } else {
 | 
	
		
			
				|  |  | -                        if self.calculate_possible(total_solutions, solutions) {
 | 
	
		
			
				|  |  | -                            return true;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                self.board[idx as usize] = 0;
 | 
	
		
			
				|  |  | -                return false;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        false
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Brute-force solver
 | 
	
		
			
				|  |  | -    /// - Prints out a (one) solution.
 | 
	
		
			
				|  |  | -    /// - Returns the number of solutions found.
 | 
	
		
			
				|  |  | -    pub fn bruteforce_solver(&self) -> u16 {
 | 
	
		
			
				|  |  | -        let mut workset = Sudoku {
 | 
	
		
			
				|  |  | -            board: self.board,
 | 
	
		
			
				|  |  | -            possible: [Bits(0); MAX_SIZE as usize],
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let mut total_solutions: u16 = 0;
 | 
	
		
			
				|  |  | -        let mut solutions = Vec::new();
 | 
	
		
			
				|  |  | -        solutions.reserve(1);
 | 
	
		
			
				|  |  | -        workset.calculate_possible(&mut total_solutions, &mut solutions);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // return number of solutions.
 | 
	
		
			
				|  |  | -        if solutions.len() > 0 {
 | 
	
		
			
				|  |  | -            println!("*** A solution:");
 | 
	
		
			
				|  |  | -            workset.board = solutions[0];
 | 
	
		
			
				|  |  | -            workset.display();
 | 
	
		
			
				|  |  | -            println!("***");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        total_solutions
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Make a sudoku puzzle.
 | 
	
		
			
				|  |  | -    /// This gives us a fully solved puzzle.
 | 
	
		
			
				|  |  | -    pub fn make(&mut self) {
 | 
	
		
			
				|  |  | -        let mut rng = ChaCha20Rng::from_entropy();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        self.fill_board(&mut rng);
 | 
	
		
			
				|  |  | -        // Ok, this gives us a random (but fully solved) puzzle.
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Fill puzzle with random
 | 
	
		
			
				|  |  | -    /// - This is like the brute-force solver, it calls itself recursively
 | 
	
		
			
				|  |  | -    ///   and backtraces if a solution can't be found.
 | 
	
		
			
				|  |  | -    fn fill_board(&mut self, rng: &mut ChaCha20Rng) -> bool {
 | 
	
		
			
				|  |  | -        let backup = Sudoku {
 | 
	
		
			
				|  |  | -            board: self.board,
 | 
	
		
			
				|  |  | -            possible: self.possible,
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for idx in 0..MAX_SIZE as usize {
 | 
	
		
			
				|  |  | -            if self.board[idx as usize] == 0 {
 | 
	
		
			
				|  |  | -                let (x, y) = xy(idx);
 | 
	
		
			
				|  |  | -                let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
 | 
	
		
			
				|  |  | -                let mut total_available: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                for t in self.possible[idx as usize].iter() {
 | 
	
		
			
				|  |  | -                    available[total_available as usize] = t;
 | 
	
		
			
				|  |  | -                    total_available += 1;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                if total_available == 0 {
 | 
	
		
			
				|  |  | -                    // No possible moves remain.
 | 
	
		
			
				|  |  | -                    /*
 | 
	
		
			
				|  |  | -                    self.board = backup.board;
 | 
	
		
			
				|  |  | -                    self.possible = backup.possible;
 | 
	
		
			
				|  |  | -                     */
 | 
	
		
			
				|  |  | -                    return false;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // Randomize the possible items.
 | 
	
		
			
				|  |  | -                available[0..total_available as usize].shuffle(rng);
 | 
	
		
			
				|  |  | -                for v_idx in 0..total_available {
 | 
	
		
			
				|  |  | -                    let value = available[v_idx as usize];
 | 
	
		
			
				|  |  | -                    self.set(x, y, value);
 | 
	
		
			
				|  |  | -                    if self.fill_board(rng) {
 | 
	
		
			
				|  |  | -                        return true;
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    // failure
 | 
	
		
			
				|  |  | -                    self.board = backup.board;
 | 
	
		
			
				|  |  | -                    self.possible = backup.possible;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                // We've run out of possible.
 | 
	
		
			
				|  |  | -                return false;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // We've visited everything, and it isn't 0.
 | 
	
		
			
				|  |  | -        return true;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Puzzle generation
 | 
	
		
			
				|  |  | -    /// - Removes a number, tests to see if the puzzle can still be solved by logic.
 | 
	
		
			
				|  |  | -    /// - Returns true when still a valid, solvable puzzle.
 | 
	
		
			
				|  |  | -    /// - Otherwise, restores number and returns false.
 | 
	
		
			
				|  |  | -    pub fn remove(&mut self) -> bool {
 | 
	
		
			
				|  |  | -        // Find a number, remove it. Save position.
 | 
	
		
			
				|  |  | -        let mut rng = ChaCha20Rng::from_entropy();
 | 
	
		
			
				|  |  | -        let puzrange = Uniform::new(0, WIDTH);
 | 
	
		
			
				|  |  | -        let mut x = 0;
 | 
	
		
			
				|  |  | -        let mut y = 0;
 | 
	
		
			
				|  |  | -        let mut value: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        while value == 0 {
 | 
	
		
			
				|  |  | -            x = puzrange.sample(&mut rng);
 | 
	
		
			
				|  |  | -            y = puzrange.sample(&mut rng);
 | 
	
		
			
				|  |  | -            value = self.get(x, y);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        self.set(x, y, 0);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // Clone, and solve by logic.
 | 
	
		
			
				|  |  | -        let mut puzcopy = self.clone();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        puzcopy.reset_possible();
 | 
	
		
			
				|  |  | -        /*
 | 
	
		
			
				|  |  | -        puzcopy.display();
 | 
	
		
			
				|  |  | -        puzcopy.display_possible();
 | 
	
		
			
				|  |  | -         */
 | 
	
		
			
				|  |  | -        // If solvable, return true.
 | 
	
		
			
				|  |  | -        while puzcopy.solve(false) {}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        /*
 | 
	
		
			
				|  |  | -        puzcopy.display();
 | 
	
		
			
				|  |  | -        puzcopy.display_possible();
 | 
	
		
			
				|  |  | -        */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if puzcopy.puzzle_complete() {
 | 
	
		
			
				|  |  | -            return true;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // If not solvable, restore number, return false.
 | 
	
		
			
				|  |  | -        self.set(x, y, value);
 | 
	
		
			
				|  |  | -        return false;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /*
 | 
	
		
			
				|  |  | -    fn make_(&mut self) {
 | 
	
		
			
				|  |  | -        self.clear();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let mut rng = ChaCha20Rng::from_entropy();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let pick_one = |this: &Self, rng: &mut ChaCha20Rng, idx: u8| -> Option<u8> {
 | 
	
		
			
				|  |  | -            let mut available: [u8; WIDTH as usize] = [0; WIDTH as usize];
 | 
	
		
			
				|  |  | -            let mut total_available: u8 = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            for t in this.possible[idx as usize].iter() {
 | 
	
		
			
				|  |  | -                available[total_available as usize] = t;
 | 
	
		
			
				|  |  | -                total_available += 1;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            if total_available == 1 {
 | 
	
		
			
				|  |  | -                return Some(available[0]);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            if total_available == 0 {
 | 
	
		
			
				|  |  | -                return None;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -            Some(available[rng.gen_range(0..total_available as usize)])
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for i in 0..MAX_SIZE {
 | 
	
		
			
				|  |  | -            let (x, y) = xy(i);
 | 
	
		
			
				|  |  | -            if self.board[i as usize] == 0 {
 | 
	
		
			
				|  |  | -                // Ok, we found a blank space.
 | 
	
		
			
				|  |  | -                let value = pick_one(self, &mut rng, i);
 | 
	
		
			
				|  |  | -                if value.is_some() {
 | 
	
		
			
				|  |  | -                    let value = value.unwrap();
 | 
	
		
			
				|  |  | -                    if DEBUG_OUTPUT {
 | 
	
		
			
				|  |  | -                        println!("Set({},{})={}", x, y, value);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    self.set(x, y, value);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    /// Solve, using logic alone
 | 
	
		
			
				|  |  | -    /// - Returns true when something was added to the board.
 | 
	
		
			
				|  |  | -    /// - Call solve until it returns false.
 | 
	
		
			
				|  |  | -    pub fn solve(&mut self, debug: bool) -> bool {
 | 
	
		
			
				|  |  | -        // Pass 1: Look for singles in the possible sets.
 | 
	
		
			
				|  |  | -        let mut found_something = false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        for i in 0..MAX_SIZE as usize {
 | 
	
		
			
				|  |  | -            if self.possible[i as usize].count_set() == 1 {
 | 
	
		
			
				|  |  | -                // Get the value
 | 
	
		
			
				|  |  | -                let value = self.possible[i as usize].iter().next().unwrap();
 | 
	
		
			
				|  |  | -                // Found one!
 | 
	
		
			
				|  |  | -                if debug {
 | 
	
		
			
				|  |  | -                    println!("Set1 {:?} to {}", xy(i), value);
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                self.set(xy(i).0, xy(i).1, value);
 | 
	
		
			
				|  |  | -                found_something = true;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        let mut g = Group::new();
 | 
	
		
			
				|  |  | -        let mut values = Bits(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]);
 | 
	
		
			
				|  |  | -                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]);
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // println!("values {:?}", values);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            // 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[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 {
 | 
	
		
			
				|  |  | -                            break;
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                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);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    this.set(xy(pos).0, xy(pos).1, v);
 | 
	
		
			
				|  |  | -                    found_something = true;
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        // Change to 0..WIDTH ...  Keep it simple.
 | 
	
		
			
				|  |  | -        for i in 0..WIDTH {
 | 
	
		
			
				|  |  | -            let mut g = for_column(i);
 | 
	
		
			
				|  |  | -            // g.for_column(i, 1);
 | 
	
		
			
				|  |  | -            group_process(self, &g);
 | 
	
		
			
				|  |  | -            g = for_row(i);
 | 
	
		
			
				|  |  | -            // g.for_row(1, i);
 | 
	
		
			
				|  |  | -            group_process(self, &g);
 | 
	
		
			
				|  |  | -            g = for_cell(i);
 | 
	
		
			
				|  |  | -            // g.for_iter(i);
 | 
	
		
			
				|  |  | -            group_process(self, &g);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if found_something {
 | 
	
		
			
				|  |  | -            return found_something;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        if debug {
 | 
	
		
			
				|  |  | -            println!("Looking for pairs...");
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        // PAIR processing.
 | 
	
		
			
				|  |  | -        for i in 0..WIDTH {
 | 
	
		
			
				|  |  | -            g.for_iter(i);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            for gidx in 0..WIDTH - 1 {
 | 
	
		
			
				|  |  | -                let gpos = g.0[gidx as usize];
 | 
	
		
			
				|  |  | -                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].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] == 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;
 | 
	
		
			
				|  |  | -                                    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.0[remove as usize];
 | 
	
		
			
				|  |  | -                                    if self.possible[rpos as usize].get(values[0]) {
 | 
	
		
			
				|  |  | -                                        self.possible[rpos as usize].set(values[0], false);
 | 
	
		
			
				|  |  | -                                        found_something = true;
 | 
	
		
			
				|  |  | -                                        pair_removed = true;
 | 
	
		
			
				|  |  | -                                    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                    if self.possible[rpos as usize].get(values[1]) {
 | 
	
		
			
				|  |  | -                                        self.possible[rpos as usize].set(values[1], 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 xy(gpos).0 == xy(fpos).0 {
 | 
	
		
			
				|  |  | -                                    // Matching X - process column
 | 
	
		
			
				|  |  | -                                    let column = xy(gpos).0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                    vpos = 0;
 | 
	
		
			
				|  |  | -                                    for z in self.possible[gpos as usize].iter() {
 | 
	
		
			
				|  |  | -                                        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]
 | 
	
		
			
				|  |  | -                                            .get(values[0])
 | 
	
		
			
				|  |  | -                                        {
 | 
	
		
			
				|  |  | -                                            self.possible[pos(column, remove) as usize]
 | 
	
		
			
				|  |  | -                                                .set(values[0], false);
 | 
	
		
			
				|  |  | -                                            found_something = true;
 | 
	
		
			
				|  |  | -                                            pair_removed = true;
 | 
	
		
			
				|  |  | -                                        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                        if self.possible[pos(column, remove) as usize]
 | 
	
		
			
				|  |  | -                                            .get(values[1])
 | 
	
		
			
				|  |  | -                                        {
 | 
	
		
			
				|  |  | -                                            self.possible[pos(column, remove) as usize]
 | 
	
		
			
				|  |  | -                                                .set(values[1], false);
 | 
	
		
			
				|  |  | -                                            found_something = true;
 | 
	
		
			
				|  |  | -                                            pair_removed = true;
 | 
	
		
			
				|  |  | -                                        }
 | 
	
		
			
				|  |  | -                                    }
 | 
	
		
			
				|  |  | -                                }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                if xy(gpos).1 == xy(fpos).1 {
 | 
	
		
			
				|  |  | -                                    // Matching Y - process row
 | 
	
		
			
				|  |  | -                                    let row = xy(gpos).1;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                    vpos = 0;
 | 
	
		
			
				|  |  | -                                    for z in self.possible[gpos as usize].iter() {
 | 
	
		
			
				|  |  | -                                        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].get(values[0]) {
 | 
	
		
			
				|  |  | -                                            self.possible[pos(remove, row) as usize]
 | 
	
		
			
				|  |  | -                                                .set(values[0], false);
 | 
	
		
			
				|  |  | -                                            found_something = true;
 | 
	
		
			
				|  |  | -                                            pair_removed = true;
 | 
	
		
			
				|  |  | -                                        }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -                                        if self.possible[pos(remove, row) as usize].get(values[1]) {
 | 
	
		
			
				|  |  | -                                            self.possible[pos(remove, row) as usize]
 | 
	
		
			
				|  |  | -                                                .set(values[1], 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
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +    
 |