use rand::{thread_rng, Rng};
use sol::PossibilityMap;
use Difficulty;
use Element;
use Grid;
use Score;
use Sudoku;
const MAX_HARDEN_ITERATIONS: u8 = 20;
pub trait Generate: Score + Sized {
fn generate(order: u8, difficulty: Difficulty) -> Self;
}
fn take_random<T>(values: &mut Vec<T>) -> Option<T> {
let mut rng = thread_rng();
let mut indices = (0..values.len()).collect::<Vec<_>>();
rng.shuffle(&mut indices);
indices.get(0).map(|index| values.remove(*index))
}
fn recurse(puzzle: Sudoku) -> Option<Sudoku> {
let map: PossibilityMap = puzzle.clone().into();
match map.next() {
(None, _) => {
let puzzle = puzzle.clone();
if puzzle.is_complete() {
Some(puzzle)
} else {
None
}
}
(Some(index), Some(set)) => {
let mut possibilities = (1..=(puzzle.order as usize).pow(2))
.filter(|v| set.contains(*v))
.collect::<Vec<_>>();
while let Some(candidate) = take_random(&mut possibilities) {
let solution = recurse(puzzle.substitute(index, Some(Element(candidate as u8))));
if solution.is_some() {
return solution;
}
}
None
}
_ => unreachable!(),
}
}
fn grid(order: u8) -> Option<Sudoku> {
let mut rng = thread_rng();
let mut puzzle = Sudoku::new(order);
{
let mut first_box = (1..=order.pow(2))
.map(|v| Some(Element(v)))
.collect::<Vec<_>>();
rng.shuffle(&mut first_box);
let order = order as usize;
let axis = order.pow(2);
{
let elements = &mut puzzle.elements;
for i in 0..axis {
let index = i / order * axis + i;
elements[index] = first_box[i];
}
}
recurse(puzzle)
}
}
fn harden(mut sudoku: &mut Sudoku, target: Difficulty) -> Result<(), ()> {
let current = sudoku.score().unwrap();
let mut points = sudoku.points();
for _ in 0..MAX_HARDEN_ITERATIONS {
if let (Some(one), Some(two)) = (take_random(&mut points), take_random(&mut points)) {
let (one, two) = (one.fold(sudoku.order), two.fold(sudoku.order));
let mut puzzle = sudoku.clone();
puzzle.elements[one] = None;
puzzle.elements[two] = None;
match puzzle.score() {
Some(score) => {
if score > current {
let difficulty: Difficulty = score.into();
if difficulty > target {
continue;
}
sudoku.elements[one] = None;
sudoku.elements[two] = None;
return if difficulty == target {
Ok(())
} else {
harden(&mut sudoku, target)
};
}
}
_ => {}
}
}
}
Err(())
}
impl Generate for Sudoku {
fn generate(order: u8, difficulty: Difficulty) -> Self {
let mut puzzle = grid(order).unwrap();
let _ = harden(&mut puzzle, difficulty);
puzzle
}
}
#[cfg(test)]
mod tests {
use gen;
use Solve;
#[cfg_attr(feature = "2D", test)]
fn test_grid() {
let grid = gen::grid(3);
let grid = grid.unwrap();
assert!(grid.is_complete());
assert!(grid.is_uniquely_solvable());
}
}