use crate::cell::{Cell, CellValue}; use std::collections::HashSet; const REGION_SIZE: usize = 9; const SUB_REGION_SIZE: usize = 3; pub struct Board { cells: Vec>, squares: Vec, rows: Vec, cols: Vec, } impl Board { pub fn new() -> Board { let mut cells = Vec::with_capacity(REGION_SIZE); for _ in 0..REGION_SIZE { let mut col = Vec::with_capacity(REGION_SIZE); for _ in 0..REGION_SIZE { col.push(Cell::default()); } cells.push(col); } let mut squares = Vec::with_capacity(REGION_SIZE); for x in 0..SUB_REGION_SIZE { for y in 0..SUB_REGION_SIZE { squares.push(Region::new_square(&Point(x, y))); } } let mut rows = Vec::with_capacity(REGION_SIZE); for x in 0..REGION_SIZE { rows.push(Region::new_row(&Point(x, 0))); } let mut cols = Vec::with_capacity(REGION_SIZE); for y in 0..REGION_SIZE { cols.push(Region::new_col(&Point(0, y))); } let board = Board { cells, squares, rows, cols }; board } pub fn value(&self, point: &Point) -> Option { self.cells[point.0][point.1].value() } pub fn set_value(&mut self, point: &Point, value: CellValue) { self.cells[point.0][point.1].set_value(value); self.update_regions(point, value); } pub fn update_regions(&mut self, point: &Point, value: CellValue) { self.update_columns(point, value); self.update_rows(point, value); self.update_squares(point, value); } pub fn update_rows(&mut self, point: &Point, value: CellValue) { for row in self.rows.iter_mut() { if row.includes(point) { for pt in row.iter_mut() { self.cells[pt.0][pt.1].remove_candidate(value); } } } } pub fn update_columns(&mut self, point: &Point, value: CellValue) { for col in self.cols.iter_mut() { if col.includes(point) { for pt in col.iter_mut() { self.cells[pt.0][pt.1].remove_candidate(value); } } } } pub fn update_squares(&mut self, point: &Point, value: CellValue) { for i in self.squares.iter_mut() { if i.includes(point) { for pt in i.iter_mut() { self.cells[pt.0][pt.1].remove_candidate(value); } } } } pub fn candidates(&self, point: &Point) -> HashSet { self.cells[point.0][point.1].candidates() } } #[derive(PartialEq, Debug)] pub struct Point(usize, usize); pub struct Region { points: Vec, } impl Region { pub fn new_square(start: &Point)-> Region { let mut points = Vec::with_capacity(REGION_SIZE); for x in start.0..(start.0 + SUB_REGION_SIZE) { for y in start.1..(start.1 + SUB_REGION_SIZE) { points.push(Point(x, y)); } } Region { points, } } pub fn new_row(start: &Point) -> Region { let mut points = Vec::with_capacity(REGION_SIZE); for y in 0..REGION_SIZE { points.push(Point(start.0, y)); } Region { points } } pub fn new_col(start: &Point) -> Region { let mut points = Vec::with_capacity(REGION_SIZE); for x in 0..REGION_SIZE { points.push(Point(x, start.1)); } Region { points } } } impl Region { fn iter(&self) -> std::slice::Iter { self.points.iter() } fn includes(&self, point: &Point) -> bool { self.points.contains(point) } fn iter_mut(&mut self) -> std::slice::IterMut { self.points.iter_mut() } } #[cfg(test)] mod test { use assertables::{assert_contains, assert_not_contains}; use super::*; #[test] fn has_none_values() { let board = Board::new(); assert!(board.value(&Point(0, 0)).is_none()); } #[test] fn set_value_updates_cell() { let mut board = Board::new(); board.set_value(&Point(0, 0), CellValue::One); let val = board.value(&Point(0, 0)).unwrap(); assert_eq!(val, CellValue::One); // Same column assert_not_contains!(board.candidates(&Point(0, 1)), &CellValue::One); // Same row assert_not_contains!(board.candidates(&Point(1, 0)), &CellValue::One); // Same sub-structure assert_not_contains!(board.candidates(&Point(1, 1)), &CellValue::One); // Different row assert_contains!(board.candidates(&Point(8, 8)), &CellValue::One); } }