use screwsat::solver::*; use std::collections::HashMap; fn lit_from_pos_and_val(n: usize, y: usize, x: usize, val: usize) -> Lit { // x_y_x_v Lit::new((n * n * y + n * x + (val - 1)) as u32, true) } /// n by n Sudoku /// Sudoku Condition: /// `x_r_w_v = true` means that the cell (r, w) is assigned to `v`(1~n) /// Conditions /// 1. one cell is either one of 1 to n.(at least) /// 2. one cell can't be assigned to multiple values.(at most) /// 3. cells that are in same row/col/block can't be assigned to same value.(at most) /// 4. cells are filled by a board value fn clauses_from_sudoku(board: &[Vec]) -> Vec> { let n: usize = board.len(); let mut cell_to_lits = HashMap::new(); for r in 0..n { for c in 0..n { // x_r_c_v let clause: Vec<_> = (1..=n) .map(|val| lit_from_pos_and_val(n, r, c, val)) .collect(); cell_to_lits.insert((r, c), clause); } } let mut clauses = vec![]; for r in 0..n { for c in 0..n { let clause = cell_to_lits.get(&(r, c)).unwrap(); // 1. clauses.push(clause.clone()); // 2. for (i, &l1) in clause.iter().enumerate() { for &l2 in clause.iter().skip(i + 1) { clauses.push(vec![!l1, !l2]); } } } } // 3. row for c in 0..n { for r1 in 0..n { for r2 in r1 + 1..n { let clause1 = cell_to_lits.get(&(r1, c)).unwrap(); let clause2 = cell_to_lits.get(&(r2, c)).unwrap(); for v in 1..=n { clauses.push(vec![!clause1[v - 1], !clause2[v - 1]]); } } } } // 3. col for r in 0..n { for c1 in 0..n { for c2 in c1 + 1..n { let clause1 = cell_to_lits.get(&(r, c1)).unwrap(); let clause2 = cell_to_lits.get(&(r, c2)).unwrap(); for v in 1..=n { clauses.push(vec![!clause1[v - 1], !clause2[v - 1]]); } } } } // 3. block let block = (n as f64).sqrt() as usize; for r_b in 0..block { for c_b in 0..block { let rs = r_b * block; let cs = c_b * block; let mut block_indices = vec![]; for r1 in 0..block { for c1 in 0..block { block_indices.push((rs + r1, cs + c1)); } } for (i, p1) in block_indices.iter().enumerate() { for p2 in block_indices.iter().skip(i + 1) { let (r1, c1) = *p1; let (r2, c2) = *p2; let clause1 = cell_to_lits.get(&(r1, c1)).unwrap(); let clause2 = cell_to_lits.get(&(r2, c2)).unwrap(); for v in 1..=9 { clauses.push(vec![!clause1[v - 1], !clause2[v - 1]]); } } } } } for y in 0..n { for x in 0..n { if board[y][x] > 0 { let lit = lit_from_pos_and_val(n, y, x, board[y][x] as usize); clauses.push(vec![lit]); } } } clauses } fn print_sudoku(board: &[Vec], colored: &[Vec]) { let n = board.len(); let mut results = vec![]; println!("Sudoku {} x {}", n, n); for y in 0..n { let line: String = board[y] .iter() .enumerate() .map(|(x, v)| { if colored[y][x] > 0 { //color red format!("\x1b[{}m{:02}\x1b[m ", 31, v) } else { format!("{:02} ", v) } }) .collect(); results.push(line); } // print results let line: String = (0..2 * n + 10).map(|_| "-").collect(); println!("{}", line); results.iter().for_each(|line| { println!("{}", line); }); println!("{}", line); println!(); } fn board_from_assign(n: usize, assigns: &[LitBool]) -> Vec> { let mut board = vec![vec![0; n]; n]; for y in 0..n { for x in 0..n { for v in 1..=n { let var = lit_from_pos_and_val(n, y, x, v).var().0 as usize; if assigns[var] == LitBool::True { board[y][x] = v as u32; break; } } } } board } fn main() { { let mut solver = Solver::default(); let sudoku_9 = [ vec![0, 0, 5, 3, 0, 0, 0, 0, 0], vec![8, 0, 0, 0, 0, 0, 0, 2, 0], vec![0, 7, 0, 0, 1, 0, 5, 0, 0], vec![4, 0, 0, 0, 0, 5, 3, 0, 0], vec![0, 1, 0, 0, 7, 0, 0, 0, 6], vec![0, 0, 3, 2, 0, 0, 0, 8, 0], vec![0, 6, 0, 5, 0, 0, 0, 0, 9], vec![0, 0, 4, 0, 0, 0, 0, 3, 0], vec![0, 0, 0, 0, 0, 9, 7, 0, 0], ]; print_sudoku(&sudoku_9, &sudoku_9); let clauses = clauses_from_sudoku(&sudoku_9); clauses.iter().for_each(|clause| solver.add_clause(clause)); let status = solver.solve(None); match status { Status::Sat => { println!("SAT"); let result = board_from_assign(sudoku_9.len(), &solver.models); print_sudoku(&result, &sudoku_9); } Status::Unsat => { println!("UNSAT"); } _ => { println!("INDETERMINATE"); } } } { let mut solver = Solver::default(); let sudoku_16 = [ vec![0, 15, 0, 1, 0, 2, 10, 14, 12, 0, 0, 0, 0, 0, 0, 0], vec![0, 6, 3, 16, 12, 0, 8, 4, 14, 15, 1, 0, 2, 0, 0, 0], vec![14, 0, 9, 7, 11, 3, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0], vec![4, 13, 2, 12, 0, 0, 0, 0, 6, 0, 0, 0, 0, 15, 0, 0], vec![0, 0, 0, 0, 14, 1, 11, 7, 3, 5, 10, 0, 0, 8, 0, 12], vec![3, 16, 0, 0, 2, 4, 0, 0, 0, 14, 7, 13, 0, 0, 5, 15], vec![11, 0, 5, 0, 0, 0, 0, 0, 0, 9, 4, 0, 0, 6, 0, 0], vec![0, 0, 0, 0, 13, 0, 16, 5, 15, 0, 0, 12, 0, 0, 0, 0], vec![0, 0, 0, 0, 9, 0, 1, 12, 0, 8, 3, 10, 11, 0, 15, 0], vec![2, 12, 0, 11, 0, 0, 14, 3, 5, 4, 0, 0, 0, 0, 9, 0], vec![6, 3, 0, 4, 0, 0, 13, 0, 0, 11, 9, 1, 0, 12, 16, 2], vec![0, 0, 10, 9, 0, 0, 0, 0, 0, 0, 12, 0, 8, 0, 6, 7], vec![12, 8, 0, 0, 16, 0, 0, 10, 0, 13, 0, 0, 0, 5, 0, 0], vec![5, 0, 0, 0, 3, 0, 4, 6, 0, 1, 15, 0, 0, 0, 0, 0], vec![0, 9, 1, 6, 0, 14, 0, 11, 0, 0, 2, 0, 0, 0, 10, 8], vec![0, 14, 0, 0, 0, 13, 9, 0, 4, 12, 11, 8, 0, 0, 2, 0], ]; print_sudoku(&sudoku_16, &sudoku_16); let clauses = clauses_from_sudoku(&sudoku_16); clauses.iter().for_each(|clause| solver.add_clause(clause)); let status = solver.solve(None); match status { Status::Sat => { println!("SAT"); let result = board_from_assign(sudoku_16.len(), &solver.models); print_sudoku(&result, &sudoku_16); } Status::Unsat => { println!("UNSAT"); } _ => { println!("INDETERMINATE"); } } } }