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
Generated
+81
View File
@@ -8,9 +8,90 @@ version = "8.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "857057651cdf1fe4bc1e8308493c752db559df0330f23b45f532f6b24c2b443d"
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "matrixmultiply"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "ndarray"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
dependencies = [
"matrixmultiply",
"num-complex",
"num-integer",
"num-traits",
"portable-atomic",
"portable-atomic-util",
"rawpointer",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "portable-atomic"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2"
[[package]]
name = "portable-atomic-util"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcdd8420072e66d54a407b3316991fe946ce3ab1083a7f575b2463866624704d"
dependencies = [
"portable-atomic",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rustdoku"
version = "0.1.0"
dependencies = [
"assertables",
"ndarray",
"num-traits",
]
+2
View File
@@ -5,3 +5,5 @@ edition = "2021"
[dependencies]
assertables = "8.18.0"
ndarray = "0.16.1"
num-traits = "0.2.19"
+56 -105
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));
}
Region { points }
fn _bounds(val: usize) -> (usize, usize) {
let mut start = val / (SUB_REGION_SIZE as usize);
start *= SUB_REGION_SIZE;
(start, start + SUB_REGION_SIZE)
}
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 row_bounds(&self) -> (usize, usize) {
Self::_bounds(self.row)
}
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);