Convert to using ndarray
This commit is contained in:
+57
-106
@@ -1,148 +1,86 @@
|
||||
use crate::cell::{Cell, CellValue};
|
||||
use std::collections::HashSet;
|
||||
use ndarray::prelude::*;
|
||||
|
||||
const REGION_SIZE: usize = 9;
|
||||
const SUB_REGION_SIZE: usize = 3;
|
||||
|
||||
pub struct Board {
|
||||
cells: Vec<Vec<Cell>>,
|
||||
squares: Vec<Region>,
|
||||
rows: Vec<Region>,
|
||||
cols: Vec<Region>,
|
||||
cells: Array2<Cell>,
|
||||
}
|
||||
|
||||
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 cells = Array2::zeros((REGION_SIZE, REGION_SIZE));
|
||||
let board = Board {
|
||||
cells,
|
||||
squares,
|
||||
rows,
|
||||
cols
|
||||
};
|
||||
board
|
||||
}
|
||||
|
||||
pub fn value(&self, point: &Point) -> Option<CellValue> {
|
||||
self.cells[point.0][point.1].value()
|
||||
self.cells[point.coordinates()].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);
|
||||
self.cells[point.coordinates()].set_value(value);
|
||||
self.update_related(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_related(&mut self, point: &Point, value: CellValue) {
|
||||
self.update_column(point, value);
|
||||
self.update_row(point, value);
|
||||
self.update_square(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_row(&mut self, point: &Point, value: CellValue) {
|
||||
for c in self.cells.slice_mut(s!(point.row, ..)) {
|
||||
c.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_column(&mut self, point: &Point, value: CellValue) {
|
||||
for c in self.cells.slice_mut(s![.., point.col]) {
|
||||
c.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 update_square(&mut self, point: &Point, value: CellValue) {
|
||||
let row_bounds = point.row_bounds();
|
||||
let col_bounds = point.col_bounds();
|
||||
for c in self.cells.slice_mut(s![row_bounds.0..row_bounds.1, col_bounds.0..col_bounds.1]) {
|
||||
c.remove_candidate(value);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn candidates(&self, point: &Point) -> HashSet<CellValue> {
|
||||
self.cells[point.0][point.1].candidates()
|
||||
self.cells[point.coordinates()].candidates()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Point(usize, usize);
|
||||
|
||||
pub struct Region {
|
||||
points: Vec<Point>,
|
||||
pub struct Point {
|
||||
row: usize,
|
||||
col: usize
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
impl Point {
|
||||
fn coordinates(&self) -> [usize; 2] {
|
||||
[self.row, self.col]
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
fn _bounds(val: usize) -> (usize, usize) {
|
||||
let mut start = val / (SUB_REGION_SIZE as usize);
|
||||
start *= SUB_REGION_SIZE;
|
||||
(start, start + SUB_REGION_SIZE)
|
||||
}
|
||||
|
||||
Region { points }
|
||||
fn row_bounds(&self) -> (usize, usize) {
|
||||
Self::_bounds(self.row)
|
||||
}
|
||||
|
||||
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<Point> {
|
||||
self.points.iter()
|
||||
}
|
||||
|
||||
fn includes(&self, point: &Point) -> bool {
|
||||
self.points.contains(point)
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> std::slice::IterMut<Point> {
|
||||
self.points.iter_mut()
|
||||
fn col_bounds(&self) -> (usize, usize) {
|
||||
Self::_bounds(self.col)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,27 +90,40 @@ mod test {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn bounds_are_correct() {
|
||||
let origin = Point{ row: 0, col: 0};
|
||||
assert_eq!(origin.row_bounds(), (0, 3));
|
||||
assert_eq!(origin.col_bounds(), (0, 3));
|
||||
let one = Point{ row: 1, col: 1};
|
||||
assert_eq!(one.row_bounds(), (0, 3));
|
||||
assert_eq!(one.col_bounds(), (0, 3));
|
||||
let five = Point{ row: 5, col: 7};
|
||||
assert_eq!(five.row_bounds(), (3, 6));
|
||||
assert_eq!(five.col_bounds(), (6, 9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_none_values() {
|
||||
let board = Board::new();
|
||||
assert!(board.value(&Point(0, 0)).is_none());
|
||||
assert!(board.value(&Point{row: 0, col: 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();
|
||||
board.set_value(&Point{row: 0, col: 0}, CellValue::One);
|
||||
let val = board.value(&Point{row: 0, col: 0}).unwrap();
|
||||
assert_eq!(val, CellValue::One);
|
||||
|
||||
// Same column
|
||||
assert_not_contains!(board.candidates(&Point(0, 1)), &CellValue::One);
|
||||
assert_not_contains!(board.candidates(&Point{row: 1, col: 0}), &CellValue::One);
|
||||
// Same row
|
||||
assert_not_contains!(board.candidates(&Point(1, 0)), &CellValue::One);
|
||||
assert_not_contains!(board.candidates(&Point{row: 0, col: 1}), &CellValue::One);
|
||||
// Same sub-structure
|
||||
assert_not_contains!(board.candidates(&Point(1, 1)), &CellValue::One);
|
||||
assert_not_contains!(board.candidates(&Point{row: 1, col: 1}), &CellValue::One);
|
||||
|
||||
// Different row
|
||||
assert_contains!(board.candidates(&Point(8, 8)), &CellValue::One);
|
||||
assert_contains!(board.candidates(&Point{row: 8, col: 8}), &CellValue::One);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-5
@@ -1,4 +1,6 @@
|
||||
use std::collections::HashSet;
|
||||
use num_traits::identities::Zero;
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Clone, Debug, Copy, Hash, Eq, PartialEq)]
|
||||
pub enum CellValue {
|
||||
@@ -13,6 +15,7 @@ pub enum CellValue {
|
||||
Nine,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Cell {
|
||||
value: Option<CellValue>,
|
||||
candidates: HashSet<CellValue>,
|
||||
@@ -36,8 +39,19 @@ impl Cell {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Cell {
|
||||
fn default() -> Cell {
|
||||
impl Add for Cell {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, _other: Self) -> Self {
|
||||
Self {
|
||||
value: self.value,
|
||||
candidates: self.candidates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for Cell {
|
||||
fn zero() -> Cell {
|
||||
let mut candidates = HashSet::new();
|
||||
candidates.insert(CellValue::One);
|
||||
candidates.insert(CellValue::Two);
|
||||
@@ -53,6 +67,10 @@ impl Default for Cell {
|
||||
candidates,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.value.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -62,13 +80,13 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn inits_empty() {
|
||||
let cell = Cell::default();
|
||||
let cell = Cell::zero();
|
||||
assert!(cell.value().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_all_candidates() {
|
||||
let cell = Cell::default();
|
||||
let cell = Cell::zero();
|
||||
assert_contains!(cell.candidates(), &CellValue::One);
|
||||
assert_contains!(cell.candidates(), &CellValue::Two);
|
||||
assert_contains!(cell.candidates(), &CellValue::Three);
|
||||
@@ -82,7 +100,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn removed_candidate_is_gone() {
|
||||
let mut cell = Cell::default();
|
||||
let mut cell = Cell::zero();
|
||||
cell.remove_candidate(CellValue::One);
|
||||
assert_not_contains!(cell.candidates(), &CellValue::One);
|
||||
assert_contains!(cell.candidates(), &CellValue::Two);
|
||||
|
||||
Reference in New Issue
Block a user