|  | @@ -2,10 +2,12 @@
 | 
	
		
			
				|  |  |  use crate::bits::*;
 | 
	
		
			
				|  |  |  use crate::group::*;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -use strum::IntoEnumIterator;
 | 
	
		
			
				|  |  |  use std::string::String;
 | 
	
		
			
				|  |  | +use strum::IntoEnumIterator;
 | 
	
		
			
				|  |  |  extern crate rand_chacha;
 | 
	
		
			
				|  |  | +use rand::distributions::{Distribution, Uniform};
 | 
	
		
			
				|  |  |  use rand::seq::SliceRandom;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  use rand_chacha::rand_core::SeedableRng;
 | 
	
		
			
				|  |  |  use rand_chacha::ChaCha20Rng;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -128,16 +130,17 @@ impl AnyBoard {
 | 
	
		
			
				|  |  |          self.board[index]
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Load from ksudoku file
 | 
	
		
			
				|  |  | -    /// - uses load_from_tld
 | 
	
		
			
				|  |  | +    /// Load from ksudoku format
 | 
	
		
			
				|  |  | +    /// - uses load_from_tld with specifics
 | 
	
		
			
				|  |  |      pub fn load_ksudoku(&mut self, s: &str) -> Result<(), Box<dyn error::Error>> {
 | 
	
		
			
				|  |  |          self.load_from_tld('b', '_', s)
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    /// Save to ksudoku format
 | 
	
		
			
				|  |  |      pub fn save_ksudoku(&self) -> String {
 | 
	
		
			
				|  |  |          self.save_to_tld('b', '_')
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /// Load puzzle from string (top,left) going down.
 | 
	
		
			
				|  |  |      pub fn load_from_tld(
 | 
	
		
			
				|  |  |          &mut self,
 | 
	
	
		
			
				|  | @@ -746,6 +749,17 @@ impl AnySolver {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    pub fn get(&self, x: u8, y: u8) -> u8 {
 | 
	
		
			
				|  |  | +        debug_assert!(
 | 
	
		
			
				|  |  | +            x < self.board.width && y < self.board.width,
 | 
	
		
			
				|  |  | +            "Expected ({}, {}) < {}",
 | 
	
		
			
				|  |  | +            x,
 | 
	
		
			
				|  |  | +            y,
 | 
	
		
			
				|  |  | +            self.board.width
 | 
	
		
			
				|  |  | +        );
 | 
	
		
			
				|  |  | +        self.board.get(x, y)
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /// Make completed board.
 | 
	
		
			
				|  |  |      /// - uses fill_board (recursive)
 | 
	
		
			
				|  |  |      pub fn make(&mut self) {
 | 
	
	
		
			
				|  | @@ -787,14 +801,6 @@ impl AnySolver {
 | 
	
		
			
				|  |  |                      // self.board.display();
 | 
	
		
			
				|  |  |                      // self.possible.display();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                    // I can't validate board, it might be invalid!
 | 
	
		
			
				|  |  | -                    // Really!
 | 
	
		
			
				|  |  | -                    /*
 | 
	
		
			
				|  |  | -                    if ! self.validate_board() {
 | 
	
		
			
				|  |  | -                        panic!("Whaaaat?!");
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    */
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |                      if self.fill_board(rng) {
 | 
	
		
			
				|  |  |                          return true;
 | 
	
		
			
				|  |  |                      }
 | 
	
	
		
			
				|  | @@ -811,6 +817,50 @@ impl AnySolver {
 | 
	
		
			
				|  |  |          true
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    /// Make a puzzle by removing items
 | 
	
		
			
				|  |  | +    /// - Verify it still has a single solution.
 | 
	
		
			
				|  |  | +    pub fn make_puzzle(&mut self) {
 | 
	
		
			
				|  |  | +        let mut rng = ChaCha20Rng::from_entropy();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if !self.board.complete() {
 | 
	
		
			
				|  |  | +            self.fill_board(&mut rng);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        self.reset_possible();
 | 
	
		
			
				|  |  | +        while self.remove(&mut rng) {}
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /// Remove an item
 | 
	
		
			
				|  |  | +    /// - Remove a number, test if still solvable.
 | 
	
		
			
				|  |  | +    /// - Returns true when removal was successful, and still solvable.
 | 
	
		
			
				|  |  | +    /// - Return false when it can't.
 | 
	
		
			
				|  |  | +    fn remove(&mut self, rng: &mut ChaCha20Rng) -> bool {
 | 
	
		
			
				|  |  | +        let puzrange = Uniform::new(0, self.board.width);
 | 
	
		
			
				|  |  | +        let mut x = 0;
 | 
	
		
			
				|  |  | +        let mut y = 0;
 | 
	
		
			
				|  |  | +        let mut value: u8 = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        while value == 0 {
 | 
	
		
			
				|  |  | +            x = puzrange.sample(rng);
 | 
	
		
			
				|  |  | +            y = puzrange.sample(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.solve_logic();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if puzcopy.board.complete() {
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // Not solvable, restore and return false.
 | 
	
		
			
				|  |  | +        self.set(x, y, value);
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      fn logic_pass1(&mut self) -> bool {
 | 
	
		
			
				|  |  |          // Pass 1: Look for singles in the possible sets.
 | 
	
		
			
				|  |  |          let mut pass1 = false;
 | 
	
	
		
			
				|  | @@ -914,24 +964,10 @@ impl AnySolver {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              for gr in Groups::iter() {
 | 
	
		
			
				|  |  |                  for i in 0..width {
 | 
	
		
			
				|  |  | -                let g = grp.group(gr, i);
 | 
	
		
			
				|  |  | +                    let g = grp.group(gr, i);
 | 
	
		
			
				|  |  |                      group_process(self, g);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            /*
 | 
	
		
			
				|  |  | -            // 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 {
 | 
	
		
			
				|  |  |                  // Ok, pass 2 found something.
 | 
	
	
		
			
				|  | @@ -962,16 +998,18 @@ impl AnySolver {
 | 
	
		
			
				|  |  |              // Pass 1: Look for singles in the possible sets.
 | 
	
		
			
				|  |  |              while result1 {
 | 
	
		
			
				|  |  |                  result1 = self.logic_pass1();
 | 
	
		
			
				|  |  | +                /*
 | 
	
		
			
				|  |  |                  if result1 {
 | 
	
		
			
				|  |  |                      println!("Pass1");
 | 
	
		
			
				|  |  |                  };
 | 
	
		
			
				|  |  | +                */
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              let mut result2 = true;
 | 
	
		
			
				|  |  |              while result2 {
 | 
	
		
			
				|  |  |                  result2 = self.logic_pass2();
 | 
	
		
			
				|  |  |                  if result2 {
 | 
	
		
			
				|  |  | -                    println!("Pass2");
 | 
	
		
			
				|  |  | +                    // println!("Pass2");
 | 
	
		
			
				|  |  |                      found_more = true;
 | 
	
		
			
				|  |  |                  };
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -1305,7 +1343,6 @@ impl AnySolver {
 | 
	
		
			
				|  |  |              */
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          self.board.complete()
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1567,51 +1604,3 @@ mod tests {
 | 
	
		
			
				|  |  |          assert!(!solver.validate_board());
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -/* 
 | 
	
		
			
				|  |  | -    /// 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;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -*/
 | 
	
		
			
				|  |  | -    
 |