//! Killer Sudoku. //! //! https://en.wikipedia.org/wiki/Killer_sudoku extern crate puzzle_solver; use puzzle_solver::{LinExpr,Puzzle,Solution,Val,VarToken}; const SQRT_SIZE: usize = 3; const SIZE: usize = 9; type Board = [[Val; SIZE]; SIZE]; type Point = (usize, usize); fn make_killer_sudoku(board: &[(Val, Vec)]) -> (Puzzle, Vec>) { let mut sys = Puzzle::new(); let vars = sys.new_vars_with_candidates_2d(SIZE, SIZE, &[1,2,3,4,5,6,7,8,9]); for y in 0..SIZE { sys.all_different(&vars[y]); } for x in 0..SIZE { sys.all_different(vars.iter().map(|row| &row[x])); } for block in 0..SIZE { let x0 = SQRT_SIZE * (block % SQRT_SIZE); let y0 = SQRT_SIZE * (block / SQRT_SIZE); sys.all_different((0..SIZE).map(|n| &vars[y0 + (n / SQRT_SIZE)][x0 + (n % SQRT_SIZE)])); } for &(total, ref points) in board.iter() { sys.equals(total, points.iter().fold(LinExpr::from(0), |sum, &(x,y)| sum + vars[y][x])); } (sys, vars) } fn print_sudoku(dict: &Solution, vars: &Vec>) { for y in 0..SIZE { if y % SQRT_SIZE == 0 { println!(); } for x in 0..SIZE { print!("{}{}", if x % SQRT_SIZE == 0 { " " } else { "" }, dict[vars[y][x]]); } println!(); } } fn verify_sudoku(dict: &Solution, vars: &Vec>, expected: &Board) { for y in 0..SIZE { for x in 0..SIZE { assert_eq!(dict[vars[y][x]], expected[y][x]); } } } #[test] fn killersudoku_wikipedia() { let puzzle = [ ( 3, vec![(0,0), (1,0)]), (15, vec![(2,0), (3,0), (4,0)]), (22, vec![(5,0), (4,1), (5,1), (4,2)]), ( 4, vec![(6,0), (6,1)]), (16, vec![(7,0), (7,1)]), (15, vec![(8,0), (8,1), (8,2), (8,3)]), (25, vec![(0,1), (1,1), (0,2), (1,2)]), (17, vec![(2,1), (3,1)]), ( 9, vec![(2,2), (3,2), (3,3)]), ( 8, vec![(5,2), (5,3), (5,4)]), (20, vec![(6,2), (7,2), (6,3)]), ( 6, vec![(0,3), (0,4)]), (14, vec![(1,3), (2,3)]), (17, vec![(4,3), (4,4), (4,5)]), (17, vec![(7,3), (6,4), (7,4)]), (13, vec![(1,4), (2,4), (1,5)]), (20, vec![(3,4), (3,5), (3,6)]), (12, vec![(8,4), (8,5)]), (27, vec![(0,5), (0,6), (0,7), (0,8)]), ( 6, vec![(2,5), (1,6), (2,6)]), (20, vec![(5,5), (5,6), (6,6)]), ( 6, vec![(6,5), (7,5)]), (10, vec![(4,6), (3,7), (4,7), (3,8)]), (14, vec![(7,6), (8,6), (7,7), (8,7)]), ( 8, vec![(1,7), (1,8)]), (16, vec![(2,7), (2,8)]), (15, vec![(5,7), (6,7)]), (13, vec![(4,8), (5,8), (6,8)]), (17, vec![(7,8), (8,8)]), ]; let expected = [ [ 2,1,5, 6,4,7, 3,9,8 ], [ 3,6,8, 9,5,2, 1,7,4 ], [ 7,9,4, 3,8,1, 6,5,2 ], [ 5,8,6, 2,7,4, 9,3,1 ], [ 1,4,2, 5,9,3, 8,6,7 ], [ 9,7,3, 8,1,6, 4,2,5 ], [ 8,2,1, 7,3,9, 5,4,6 ], [ 6,5,9, 4,2,8, 7,1,3 ], [ 4,3,7, 1,6,5, 2,8,9 ] ]; let (mut sys, vars) = make_killer_sudoku(&puzzle); let dict = sys.solve_any().expect("solution"); print_sudoku(&dict, &vars); verify_sudoku(&dict, &vars, &expected); println!("killersudoku_wikipedia: {} guesses", sys.num_guesses()); }