1
0
Fork 0
sudoku_constraint_solver/src/rules.rs

116 lines
3.2 KiB
Rust

use std::collections::HashSet;
use crate::board::Board;
/**
Checks whether a board is valid according to the rules of the game
"Unruly" from Simon Tathams Portable Puzzle Collection.
Only works on boards which have an even number of tiles in width and height,
and which have only two options.
*/
pub fn valid_board_unruly(board: &Board) -> bool {
debug_assert!(board.get_num_options() == 2);
debug_assert!(board.get_width() % 2 == 0);
debug_assert!(board.get_height() % 2 == 0);
fn valid_window(win: &[Option<usize>]) -> bool {
win[0].zip(win[1]).map_or(true, |(x, y)| x != y)
|| win[1].zip(win[2]).map_or(true, |(x, y)| x != y)
}
// check each column and row:
// * may only contain n/2 number of one option
// * may not contain more than 2 consecutives
for x in 0..board.get_width() {
let col = board.get_column(x);
for opt in [0, 1] {
if col.iter().filter(|&&x| x == Some(opt)).count() > (board.get_width() / 2) {
log::warn!("col {x} invalid: too many of {opt}");
return false;
}
}
if !col.windows(3).all(valid_window) {
log::warn!("col {x} invalid: invalid window");
return false;
}
}
for y in 0..board.get_height() {
let row = board.get_row(y);
for opt in [0, 1] {
if row.iter().filter(|&&x| x == Some(opt)).count() > (board.get_width() / 2) {
log::warn!("row {y} invalid: too many of {opt}");
return false;
}
}
if !row.windows(3).all(valid_window) {
log::warn!("row {y} invalid: invalid window");
return false;
}
}
true
}
/**
Checks whether a board is valid according to the rules of ordinary 9x9 Sudoku:
Numbers must be unique in row, column and each 3x3 cell.
*/
pub fn valid_board_sudoku(board: &Board) -> bool {
debug_assert!(board.get_num_options() == 9);
debug_assert!(board.get_width() == 9);
debug_assert!(board.get_height() == 9);
fn get_cell(board: &Board, start_x: usize, start_y: usize) -> Vec<Option<usize>> {
(start_x..start_x + 3)
.flat_map(|x| (start_y..start_y + 3).map(|y| (x, y)).collect::<Vec<_>>())
.map(|(x, y)| board.get_tile(x, y))
.collect()
}
// check columns: must be unique
for x in 0..9 {
let mut set = HashSet::new();
if !board
.get_column(x)
.iter()
.filter_map(|&x| x)
.all(|x| set.insert(x))
{
return false;
}
}
// check rows: must be unique
for y in 0..9 {
let mut set = HashSet::new();
if !board
.get_row(y)
.iter()
.filter_map(|&x| x)
.all(|x| set.insert(x))
{
return false;
}
}
// check 3x3 cells: must be unique
for x in [0, 3, 6] {
for y in [0, 3, 6] {
let mut set = HashSet::new();
if !get_cell(board, x, y)
.iter()
.filter_map(|&x| x)
.all(|x| set.insert(x))
{
return false;
}
}
}
true
}