Convert to using ndarray

This commit is contained in:
Greg Hellings
2024-10-16 08:45:37 -05:00
parent 9af499c585
commit d431a9378a
4 changed files with 163 additions and 111 deletions
+57 -106
View File
@@ -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
View File
@@ -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);