\$\begingroup\$

A Sudoku solver with an approach similar to my previous post.

use std::collections::BTreeSet; use std::iter::FromIterator; use std::collections::HashMap; pub struct Sudoku { pub board: [i32; 81], pub empty_slots: Vec<usize>, pub board_coords: [(usize, usize); 81], pub board_cols: [[usize; 9]; 9], pub board_rows: [[usize; 9]; 9], pub board_blocks: [[usize; 9]; 9], } impl Sudoku { pub fn print_board(&self) { use std::fmt::Write; let mut s = String::new(); for i in 0..81 { write!(&mut s, "{:>3}", self.board[i]); if i % 9 == 8 { write!(&mut s, "{}", '

'); } } println!("{}", s); } // given coordinates, returns actual index in the array pub fn index(row: usize, col: usize) -> usize { row * 9 + col } // given coordinates, return block number pub fn block(row: usize, col: usize) -> usize { let r = row / 3; let c = col / 3; r * 3 + c } // given coordinates, return index inside its block pub fn index_in_block(row: usize, col: usize) -> usize { let r = row % 3; let c = col % 3; r * 3 + c } pub fn get_number(&self, row: usize, col: usize) -> i32 { let index = Sudoku::index(row, col); self.board[index] } pub fn set_number(&mut self, row: usize, col: usize, value: i32) -> bool { if self.is_valid(row, col, value) { self.set_number_no_check(row, col, value) } else { false } } pub fn set_number_no_check(&mut self, row: usize, col: usize, value: i32) -> bool { let index = Sudoku::index(row, col); self.board[index] = value; true } pub fn is_valid(&self, row: usize, col: usize, value: i32) -> bool { let index = Sudoku::index(row, col); // check that value is unique in its col for i in &self.board_cols[col] { if i == &index { continue; } if value == self.board[*i] { return false; } } // check that value is unique in its row for i in &self.board_rows[row] { if i == &index { continue; } if value == self.board[*i] { return false; } } // check that value is unique in its block let b = Sudoku::block(row, col); for i in &self.board_blocks[b] { if i == &index { continue; } if value == self.board[*i] { return false; } } true } pub fn find_solution(&self, row: usize, col: usize) -> Option<i32> { let current_number = self.get_number(row, col); // all possibilities failed if current_number == 9 { return None; } let start = if current_number == -1 { 1 } else { current_number + 1 }; for i in start..10 { if self.is_valid(row, col, i) { return Some(i); } } None } pub fn solve(&mut self) { let mut i = 0; loop { println!("================================"); self.print_board(); if i >= self.empty_slots.len() { break; } let (row, col) = self.board_coords[self.empty_slots[i]]; match self.find_solution(row, col) { Some(solution) => { self.set_number_no_check(row, col, solution); i += 1; }, None => { // no solution found, reset the grid self.set_number_no_check(row, col, -1); match i.checked_sub(1) { Some(j) => i = j, None => panic!("No solution!") } } } } } } impl FromIterator<(usize, i32)> for Sudoku { fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item=(usize, i32)> { // -1 stands for empty let mut board = [-1; 81]; let mut filled_slots = BTreeSet::new(); for (index, number) in iter { board[index] = number; filled_slots.insert(index); } let mut empty_slots: Vec<_> = (0..81).into_iter().collect(); empty_slots.retain(|x| ! filled_slots.contains(x)); let board_coords = { fn coord(index: usize) -> (usize, usize) { let row = index / 9; let col = index % 9; (row, col) } let mut map = [(0usize, 0usize); 81]; for index in 0..81 { map[index] = coord(index); } map }; // what indices are in each col? let board_rows = { let mut map = [[0usize; 9]; 9]; // kth row, ith col for k in 0..9 { for i in 0..9 { map[k][i] = Sudoku::index(k, i); } } map }; // what indices are in each col? let board_cols = { let mut map = [[0usize; 9]; 9]; // kth row, ith col for k in 0..9 { for i in 0..9 { map[i][k] = Sudoku::index(k, i); } } map }; let board_blocks = { let mut map = [[0usize; 9]; 9]; for k in 0..9 { for i in 0..9 { let index = Sudoku::index(k, i); let block = Sudoku::block(k, i); let index_in_block = Sudoku::index_in_block(k, i); map[block][index_in_block] = index; } } map }; Sudoku { board: board, empty_slots: empty_slots, board_cols: board_cols, board_rows: board_rows, board_coords: board_coords, board_blocks: board_blocks, } } } #[test] fn test_sudoku_from_iter() { let sudoku = Sudoku::from_iter(vec![(0, 1), (1, 2), (2, 3)]); assert!(! sudoku.empty_slots.contains(&0usize)); assert!(! sudoku.empty_slots.contains(&1usize)); assert!(! sudoku.empty_slots.contains(&2usize)); assert!(sudoku.empty_slots.contains(&3usize)); assert!(sudoku.empty_slots.contains(&4usize)); assert!(sudoku.empty_slots.contains(&5usize)); assert_eq!(sudoku.board[0], 1); assert_eq!(sudoku.board[1], 2); assert_eq!(sudoku.board[2], 3); assert_eq!(sudoku.board[3], -1); assert_eq!(sudoku.board[4], -1); assert_eq!(sudoku.board[5], -1); assert_eq!(sudoku.board[6], -1); assert_eq!(sudoku.board[7], -1); } #[test] fn test_access() { assert_eq!(Sudoku::index(0, 0), 0usize); assert_eq!(Sudoku::index(0, 1), 1usize); assert_eq!(Sudoku::index(0, 4), 4usize); assert_eq!(Sudoku::index(0, 8), 8usize); assert_eq!(Sudoku::index(1, 0), 9usize); assert_eq!(Sudoku::index(1, 1), 10usize); assert_eq!(Sudoku::index(1, 4), 13usize); assert_eq!(Sudoku::index(1, 8), 17usize); let mut sudoku = Sudoku::from_iter(vec![(0, 1), (13, 2), (17, 3)]); let block0 = sudoku.board_blocks[0]; let block0a = [0usize, 1, 2, 9, 10, 11, 18, 19, 20]; assert_eq!(&block0, &block0a); let block1 = sudoku.board_blocks[1]; let block1a = [3usize, 4, 5, 12, 13, 14, 21, 22, 23]; assert_eq!(&block1, &block1a); let block3 = sudoku.board_blocks[3]; let block3a = [27usize, 28, 29, 36, 37, 38, 45, 46, 47]; assert_eq!(&block3, &block3a); assert_eq!(&sudoku.board_cols[0], &[0, 9, 18, 27, 36, 45, 54, 63, 72]); assert_eq!(&sudoku.board_cols[8], &[8, 17, 26, 35, 44, 53, 62, 71, 80]); assert_eq!(&sudoku.board_rows[0], (0usize..9).collect::<Vec<_>>().as_slice()); assert_eq!(&sudoku.board_rows[8], (72usize..81).collect::<Vec<_>>().as_slice()); assert_eq!(sudoku.get_number(0, 0), 1); assert_eq!(sudoku.get_number(1, 4), 2); assert_eq!(sudoku.get_number(1, 8), 3); sudoku.set_number(0, 0, 8); assert!(! sudoku.set_number(1, 4, 3)); // 3 is invalid in this position, conflicts with (1, 8) sudoku.set_number(1, 8, 7); assert_eq!(sudoku.get_number(0, 0), 8); assert_eq!(sudoku.get_number(1, 4), 2); assert_eq!(sudoku.get_number(1, 8), 7); } #[test] fn test_valid() { let mut sudoku = Sudoku::from_iter(vec![(0, 1), (13, 2), (17, 3)]); assert!(! sudoku.is_valid(4, 0, 1)); assert!(! sudoku.is_valid(2, 2, 1)); assert!(! sudoku.is_valid(0, 5, 1)); assert!(! sudoku.is_valid(2, 4, 2)); assert!(! sudoku.is_valid(2, 5, 2)); assert!(! sudoku.is_valid(0, 3, 2)); assert!(! sudoku.is_valid(0, 5, 2)); assert!(! sudoku.is_valid(3, 4, 2)); assert!(! sudoku.is_valid(4, 4, 2)); assert!(! sudoku.is_valid(2, 6, 3)); assert!(! sudoku.is_valid(5, 8, 3)); } #[test] fn test_find_solution() { let mut sudoku = Sudoku::from_iter(vec![(0, 1), (13, 2), (17, 3)]); let s = sudoku.find_solution(0, 1); assert_eq!(s, Some(2)); sudoku.set_number(0, 1, 2); let s = sudoku.find_solution(0, 2); assert_eq!(s, Some(3)); sudoku.set_number(0, 2, 3); sudoku.set_number(1, 4, 5); sudoku.set_number(1, 8, 9); let s = sudoku.find_solution(1, 0); assert_eq!(s, Some(4)); sudoku.set_number(1, 0, 4); let s = sudoku.find_solution(1, 1); assert_eq!(s, Some(6)); sudoku.set_number(1, 1, 6); let s = sudoku.find_solution(1, 2); assert_eq!(s, Some(7)); sudoku.set_number(1, 2, 7); sudoku.set_number(3, 3, 4); sudoku.set_number(5, 6, 4); sudoku.set_number(6, 2, 4); let s = sudoku.find_solution(1, 3); assert_eq!(s, Some(1)); sudoku.set_number(1, 3, 1); } #[test] fn test_solve() { let mut sudoku = Sudoku::from_iter(vec![(0, 1), (13, 4), (17, 9)]); sudoku.solve(); for i in 0..9 { for j in 0..9 { let n = sudoku.get_number(i, j); assert!(sudoku.is_valid(i, j, n)); } } }

All suggestions are welcome.