Convert to using ndarray
This commit is contained in:
Generated
+81
@@ -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",
|
||||
]
|
||||
|
||||
@@ -5,3 +5,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
assertables = "8.18.0"
|
||||
ndarray = "0.16.1"
|
||||
num-traits = "0.2.19"
|
||||
+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