extern crate sudoku;
use sudoku::Sudoku;
fn read_sudokus(sudokus_str: &str) -> Vec<Sudoku> {
sudokus_str.lines()
.map(|line| Sudoku::from_str_line(line).unwrap_or_else(|err| panic!("{:?}", err)))
.collect()
}
#[test]
fn solve_1() {
let sudoku_str =
"___2___63
3____54_1
__1__398_
_______9_
___538___
_3_______
_263__5__
5_37____8
47___1___";
let mut sudoku = Sudoku::from_str_block(sudoku_str).unwrap();
sudoku.solve();
println!("{}", sudoku);
}
#[test]
fn solve_2() {
let sudoku_str = "\
7__|4__|__2 comment
21_|3_5|46_
__9|_28|__1
----------- comment
___|542|3__
___|___|___
__5|817|___
-----------
5__|73_|9__
_63|2_4|_17
8__|__9|__3";
let mut sudoku = Sudoku::from_str_block(sudoku_str).unwrap();
sudoku.solve();
println!("{}", sudoku);
}
#[test]
fn readme() {
let sudoku_str = "\
___|2__|_63
3__|__5|4_1
__1|__3|98_
---+---+---
___|___|_9_
___|538|___
_3_|___|___
---+---+---
_26|3__|5__
5_3|7__|__8
47_|__1|___";
let sudoku_str2 = "...2...633....54.1..1..398........9....538....3........263..5..5.37....847...1...";
let mut sudoku = Sudoku::from_str_block(sudoku_str).unwrap();
let mut sudoku2 = Sudoku::from_str_line(sudoku_str2).unwrap();
sudoku.solve();
sudoku2.solve();
println!("{}", sudoku);
println!("{}", sudoku.to_str_line());
assert!(sudoku == sudoku2);
}
#[test]
#[should_panic]
fn wrong_format_1() {
let sudoku_str =
"___2___63
3____54_1
__1__398_
_______9_
___538___
_3_______
_263__5__
5_37____8";
Sudoku::from_str_block(sudoku_str).unwrap();
}
#[test]
fn solutionless_sudokus() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/invalid_sudokus.txt") );
for sudoku in sudokus {
assert!(sudoku.solve_one().is_none());
}
}
#[test]
fn is_solved_on_unsolved() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/easy_sudokus.txt") );
for sudoku in sudokus {
assert!(!sudoku.is_solved());
}
}
#[test]
fn is_solved_on_solved() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/solved_easy_sudokus.txt") );
for sudoku in sudokus {
assert!(sudoku.is_solved());
}
}
#[test]
#[should_panic]
fn solve_unique_multiple_solutions() {
let sudoku = Sudoku::from_bytes([0; 81]).unwrap();
sudoku.solve_unique().unwrap();
}
#[test]
fn correct_solution_easy_sudokus() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/easy_sudokus.txt") );
let solved_sudokus = read_sudokus( include_str!("../sudokus/Lines/solved_easy_sudokus.txt") );
for (i, (sudoku, solved_sudoku)) in sudokus.into_iter().zip(solved_sudokus).enumerate() {
let solutions = sudoku.solve_at_most(2);
match solutions.len() {
1 => assert_eq!( solved_sudoku, solutions[0]),
0 => panic!("Found no solution for {}. sudoku:\n{}", i, sudoku.to_str_line()),
_ => panic!("Found multiple solutions for {}. sudoku\n{})", i, sudoku.to_str_line()),
}
}
}
#[test]
fn correct_solution_medium_sudokus() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/medium_sudokus.txt") );
let solved_sudokus = read_sudokus( include_str!("../sudokus/Lines/solved_medium_sudokus.txt") );
for (i, (sudoku, solved_sudoku)) in sudokus.into_iter().zip(solved_sudokus).enumerate() {
let solutions = sudoku.solve_at_most(2);
match solutions.len() {
1 => assert_eq!( solved_sudoku, solutions[0]),
0 => panic!("Found no solution for {}. sudoku:\n{}", i, sudoku.to_str_line()),
_ => panic!("Found multiple solutions for {}. sudoku\n{})", i, sudoku.to_str_line()),
}
}
}
#[test]
fn correct_solution_hard_sudokus() {
let sudokus = read_sudokus( include_str!("../sudokus/Lines/hard_sudokus.txt") );
let solved_sudokus = read_sudokus( include_str!("../sudokus/Lines/solved_hard_sudokus.txt") );
let mut no_solution_sudokus = vec![];
for (i, (sudoku, solved_sudoku)) in sudokus.into_iter().zip(solved_sudokus).enumerate() {
let solutions = sudoku.solve_at_most(2);
match solutions.len() {
1 => assert_eq!( solved_sudoku, solutions[0]),
0 => no_solution_sudokus.push((i, sudoku)), _ => panic!("Found multiple solutions for {}. sudoku\n{})", i, sudoku.to_str_line()),
}
}
if !no_solution_sudokus.is_empty() {
println!("Found no solution for the following sudokus:");
for (i, sudoku) in no_solution_sudokus {
println!("{:2}: {}", i, sudoku.to_str_line());
}
panic!();
}
}
#[test]
fn generate_filled_sudoku_correctness() {
for _ in 0..1000 {
let sudoku = Sudoku::generate_filled();
let solved_sudoku = sudoku.solve_one();
if solved_sudoku.is_none() {
panic!("Randomly generated an invalid sudoku. Please save the sudoku for debugging:\n{}", sudoku.to_str_line());
}
}
}
#[test]
fn generate_unique_sudoku_uniqueness() {
for _ in 0..100 {
let sudoku = Sudoku::generate_unique();
let solved_sudoku = sudoku.solve_unique();
if solved_sudoku.is_none() {
panic!("Randomly generated a non-proper sudoku. Please save the sudoku for debugging:\n{}", sudoku.to_str_line());
}
}
}
#[test]
fn shuffle_unsolved() {
let sudoku = Sudoku::generate_unique();
test_shuffle_sudoku(sudoku);
}
#[test]
fn shuffle_solved() {
let sudoku = Sudoku::generate_filled();
test_shuffle_sudoku(sudoku);
}
fn test_shuffle_sudoku(sudoku: Sudoku) {
let mut sudokus = vec![sudoku; 1000];
let n_clues = sudoku.n_clues();
sudokus.iter_mut().for_each(Sudoku::shuffle);
sudokus.sort();
let mut duplicates = vec![];
for (i, sudokus) in sudokus.windows(2).enumerate() {
if let [sudoku1, sudoku2] = sudokus {
assert_eq!(sudoku1.n_clues(), n_clues);
if sudoku1 == sudoku2 {
duplicates.push(i);
}
} else {
unreachable!();
}
}
if duplicates.len() > 0 {
for i in duplicates {
println!("sudoku nr {} and next: {}", i, sudokus[i].to_str_line());
}
panic!("\nRandomly shuffled a sudoku into the above equal sudoku(s). This is possible, but very unlikely. Please save the sudoku(s) for debugging.");
}
}
#[test]
fn parse_permissive() {
let sudokus = [
r"
4 . . |. . . |8 . 5
. 3 . |. . . |. . .
. . . |7 . . |. . .
------+------+------
. 2 . |. . . |. 6 .
. . . |. 8 . |4 . .
. . . |. 1 . |. . .
------+------+------
. . . |6 . 3 |. 7 .
5 . . |2 . . |. . .
1 . 4 |. . . |. . .
",
r"
Grid 01
003020600
900305001
001806400
008102900
700000008
006708200
002609500
800203009
005010300
Grid 02
200080300
060070084
030500209
000105408
000000000
402706000
301007040
720040060
004010003
",
];
let sudokus_line = [
"4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......",
"..3.2.6..9..3.5..1..18.64....81.29..7.......8..67.82....26.95..8..2.3..9..5.1.3..",
];
for (sudoku, line_sudoku) in sudokus.iter().zip(sudokus_line.iter()) {
let sudoku1 = Sudoku::from_str_block_permissive(sudoku).expect("permissive block parse error");
let sudoku2 = Sudoku::from_str_line(line_sudoku).expect("line parse error");
assert!(sudoku1 == sudoku2);
}
}
#[allow(unused)]
fn print_line() {
let sudoku = Sudoku::from_bytes([0; 81]).unwrap();
let line = sudoku.to_str_line();
let dereffed_line: &str = &line;
println!("{}", line);
}