|
@@ -0,0 +1,376 @@
|
|
|
+use crate::{Coord, Rect};
|
|
|
+
|
|
|
+/// A `Board` represents a two-dimensional grid with a fixed width and
|
|
|
+/// height. Indexing always starts from (0, 0)
|
|
|
+#[derive(PartialEq, Eq, Debug)]
|
|
|
+pub struct Board<T> {
|
|
|
+ width: usize,
|
|
|
+ height: usize,
|
|
|
+ storage: Vec<T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> Board<T> {
|
|
|
+ pub fn new_from(width: usize, height: usize, mut func: impl FnMut(usize, usize) -> T) -> Board<T> {
|
|
|
+ let mut storage = Vec::with_capacity(width * height);
|
|
|
+ for y in 0..height {
|
|
|
+ for x in 0..width {
|
|
|
+ storage.push(func(x, y))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Board { width, height, storage }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns a reference to an element at the given location, or
|
|
|
+ /// `None` if the location is out of bounds.
|
|
|
+ pub fn get(&self, x: usize, y: usize) -> Option<&T> {
|
|
|
+ if !self.contains([x, y]) {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ let idx = x + self.width * y;
|
|
|
+ self.storage.get(idx)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns a mutable reference to an element at the given
|
|
|
+ /// location, or `None` if the location is out of bounds.
|
|
|
+ pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
|
|
|
+ if !self.contains([x, y]) {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ let idx = x + self.width * y;
|
|
|
+ self.storage.get_mut(idx)
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the width of the board
|
|
|
+ pub fn width(&self) -> usize {
|
|
|
+ self.width
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns the height of the board
|
|
|
+ pub fn height(&self) -> usize {
|
|
|
+ self.height
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns `true` if the given position is in-bounds in the
|
|
|
+ /// board; otherwise returns false
|
|
|
+ pub fn contains(&self, pos: impl Into<Coord>) -> bool {
|
|
|
+ let pos = pos.into();
|
|
|
+ pos.x < self.width && pos.y < self.height
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn contains_rect(&self, rect: Rect) -> bool {
|
|
|
+ let max_x = rect.width() + rect.x() - 1;
|
|
|
+ let max_y = rect.height() + rect.y() - 1;
|
|
|
+ println!("checking if ({}, {}) is contained", max_x, max_y);
|
|
|
+ self.contains([max_x, max_y])
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns an iterator over the board in row-major order; this
|
|
|
+ /// iterator returns not only the values contained in the board,
|
|
|
+ /// but also their indices.
|
|
|
+ pub fn iter(&self) -> BoardIter<T> {
|
|
|
+ BoardIter {
|
|
|
+ n: 0,
|
|
|
+ width: self.width,
|
|
|
+ iter: self.storage.iter(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// Returns an iterator over the board in row-major order; this
|
|
|
+ /// iterator returns not only the values contained in the board,
|
|
|
+ /// but also their indices.
|
|
|
+ pub fn iter_mut(&mut self) -> BoardIterMut<T> {
|
|
|
+ BoardIterMut {
|
|
|
+ n: 0,
|
|
|
+ width: self.width,
|
|
|
+ iter: self.storage.iter_mut(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn window_iter(&self, window: Rect) -> Option<BoardWindowIter<T>> {
|
|
|
+ if !self.contains_rect(window) {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ Some(BoardWindowIter {
|
|
|
+ n: 0,
|
|
|
+ width: self.width,
|
|
|
+ window,
|
|
|
+ iter: self.storage.iter(),
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn window_iter_mut(&mut self, window: Rect) -> Option<BoardWindowIterMut<T>> {
|
|
|
+ if !self.contains_rect(window) {
|
|
|
+ return None;
|
|
|
+ }
|
|
|
+ Some(BoardWindowIterMut {
|
|
|
+ n: 0,
|
|
|
+ width: self.width,
|
|
|
+ window,
|
|
|
+ iter: self.storage.iter_mut(),
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::Index<Coord> for Board<T> {
|
|
|
+ type Output = T;
|
|
|
+
|
|
|
+ fn index(&self, pos: Coord) -> &Self::Output {
|
|
|
+ self.get(pos.x, pos.y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", pos))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::Index<(usize, usize)> for Board<T> {
|
|
|
+ type Output = T;
|
|
|
+
|
|
|
+ fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
|
|
|
+ self.get(x, y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", (x, y)))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::Index<[usize;2]> for Board<T> {
|
|
|
+ type Output = T;
|
|
|
+
|
|
|
+ fn index(&self, [x, y]: [usize;2]) -> &Self::Output {
|
|
|
+ self.get(x, y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", (x, y)))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::IndexMut<Coord> for Board<T> {
|
|
|
+ fn index_mut(&mut self, pos: Coord) -> &mut Self::Output {
|
|
|
+ self.get_mut(pos.x, pos.y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", pos))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::IndexMut<(usize, usize)> for Board<T> {
|
|
|
+ fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Self::Output {
|
|
|
+ self.get_mut(x, y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", (x, y)))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<T> std::ops::IndexMut<[usize;2]> for Board<T> {
|
|
|
+ fn index_mut(&mut self, [x, y]: [usize;2]) -> &mut Self::Output {
|
|
|
+ self.get_mut(x, y).unwrap_or_else(|| panic!("Coordinate {:?} out of range", (x, y)))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/// An iterator over the elements of a `Board`. This returns values in
|
|
|
+/// row-major order but each element is also accompanied by its
|
|
|
+/// indices.
|
|
|
+pub struct BoardIter<'a, T> {
|
|
|
+ n: usize,
|
|
|
+ width: usize,
|
|
|
+ iter: std::slice::Iter<'a, T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a, T> Iterator for BoardIter<'a, T> {
|
|
|
+ type Item = (usize, usize, &'a T);
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ if let Some(v) = self.iter.next() {
|
|
|
+ let x = self.n % self.width;
|
|
|
+ let y = self.n / self.width;
|
|
|
+ self.n += 1;
|
|
|
+ Some((x, y, v))
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/// An iterator over the elements of a `Board`. This returns values in
|
|
|
+/// row-major order but each element is also accompanied by its
|
|
|
+/// indices.
|
|
|
+pub struct BoardIterMut<'a, T> {
|
|
|
+ n: usize,
|
|
|
+ width: usize,
|
|
|
+ iter: std::slice::IterMut<'a, T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a, T> Iterator for BoardIterMut<'a, T> {
|
|
|
+ type Item = (usize, usize, &'a mut T);
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ if let Some(v) = self.iter.next() {
|
|
|
+ let x = self.n % self.width;
|
|
|
+ let y = self.n / self.width;
|
|
|
+ self.n += 1;
|
|
|
+ Some((x, y, v))
|
|
|
+ } else {
|
|
|
+ None
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+pub struct BoardWindowIter<'a, T> {
|
|
|
+ n: usize,
|
|
|
+ width: usize,
|
|
|
+ window: Rect,
|
|
|
+ iter: std::slice::Iter<'a, T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a, T> Iterator for BoardWindowIter<'a, T> {
|
|
|
+ type Item = (usize, usize, &'a T);
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ while let Some(v) = self.iter.next() {
|
|
|
+ let x = self.n % self.width;
|
|
|
+ let y = self.n / self.width;
|
|
|
+ self.n += 1;
|
|
|
+ if self.window.contains([x, y]) {
|
|
|
+ return Some((x, y, v));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ None
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+pub struct BoardWindowIterMut<'a, T> {
|
|
|
+ n: usize,
|
|
|
+ width: usize,
|
|
|
+ window: Rect,
|
|
|
+ iter: std::slice::IterMut<'a, T>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<'a, T> Iterator for BoardWindowIterMut<'a, T> {
|
|
|
+ type Item = (usize, usize, &'a mut T);
|
|
|
+
|
|
|
+ fn next(&mut self) -> Option<Self::Item> {
|
|
|
+ while let Some(v) = self.iter.next() {
|
|
|
+ let x = self.n % self.width;
|
|
|
+ let y = self.n / self.width;
|
|
|
+ self.n += 1;
|
|
|
+ if self.window.contains([x, y]) {
|
|
|
+ return Some((x, y, v));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ None
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod test {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ macro_rules! board_from_vec {
|
|
|
+ ($w:expr, $h:expr; [$($vec:tt)*]) => {
|
|
|
+ {
|
|
|
+ let w = $w;
|
|
|
+ let h = $h;
|
|
|
+ let v = vec![$($vec)*];
|
|
|
+ assert!(v.len() == w * h);
|
|
|
+ Board::new_from(w, h, |x, y| {
|
|
|
+ v[x + y * w]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn board_indexing() {
|
|
|
+ let b = board_from_vec![2,2; [1,2,3,4]];
|
|
|
+ // in-bounds tests
|
|
|
+ assert_eq!(b.get(0, 0), Some(&1));
|
|
|
+ assert_eq!(b.get(1, 0), Some(&2));
|
|
|
+ assert_eq!(b.get(0, 1), Some(&3));
|
|
|
+ assert_eq!(b.get(1, 1), Some(&4));
|
|
|
+ // out-of-bounds tests
|
|
|
+ assert_eq!(b.get(2, 0), None);
|
|
|
+ assert_eq!(b.get(0, 2), None);
|
|
|
+ assert_eq!(b.get(2, 2), None);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn board_mut_indexing() {
|
|
|
+ let mut b = board_from_vec![2,2; [1,2,3,4]];
|
|
|
+ // in-bounds tests
|
|
|
+ *b.get_mut(0, 0).unwrap() = 5;
|
|
|
+ *b.get_mut(1, 0).unwrap() = 6;
|
|
|
+ *b.get_mut(0, 1).unwrap() = 7;
|
|
|
+ *b.get_mut(1, 1).unwrap() = 8;
|
|
|
+
|
|
|
+ assert_eq!(b, board_from_vec![2,2; [5,6,7,8]]);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn board_iter() {
|
|
|
+ let b = board_from_vec![2,2; [1,2,3,4]];
|
|
|
+ let mut iter = b.iter();
|
|
|
+ // in-bounds tests
|
|
|
+ assert_eq!(iter.next(), Some((0, 0, &1)));
|
|
|
+ assert_eq!(iter.next(), Some((1, 0, &2)));
|
|
|
+ assert_eq!(iter.next(), Some((0, 1, &3)));
|
|
|
+ assert_eq!(iter.next(), Some((1, 1, &4)));
|
|
|
+ assert_eq!(iter.next(), None);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn board_iter_mut() {
|
|
|
+ let mut b = board_from_vec![2,2; [1,2,3,4]];
|
|
|
+ // in-bounds tests
|
|
|
+ for (x, y, r) in b.iter_mut() {
|
|
|
+ *r = x * 2 + y * 2;
|
|
|
+ }
|
|
|
+ assert_eq!(b, board_from_vec![2,2; [0,2,2,4]]);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn window_iter() {
|
|
|
+ let b = board_from_vec![
|
|
|
+ 4,4;
|
|
|
+ [1,2,3,4,
|
|
|
+ 5,6,7,8,
|
|
|
+ 8,7,6,5,
|
|
|
+ 4,3,2,1,
|
|
|
+ ]
|
|
|
+ ];
|
|
|
+
|
|
|
+ let mut iter = b.window_iter(
|
|
|
+ Rect {
|
|
|
+ origin: [1, 1].into(),
|
|
|
+ size: [2, 2].into(),
|
|
|
+ }).expect("Did not find expected BoardWindowIter");
|
|
|
+ // in-bounds tests
|
|
|
+ assert_eq!(iter.next(), Some((1, 1, &6)));
|
|
|
+ assert_eq!(iter.next(), Some((2, 1, &7)));
|
|
|
+ assert_eq!(iter.next(), Some((1, 2, &7)));
|
|
|
+ assert_eq!(iter.next(), Some((2, 2, &6)));
|
|
|
+ assert_eq!(iter.next(), None);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn window_iter_mut() {
|
|
|
+ let mut b: Board<isize> = board_from_vec![
|
|
|
+ 4,4;
|
|
|
+ [1,2,3,4,
|
|
|
+ 5,6,7,8,
|
|
|
+ 8,7,6,5,
|
|
|
+ 4,3,2,1,
|
|
|
+ ]];
|
|
|
+
|
|
|
+ let iter = b.window_iter_mut(
|
|
|
+ Rect {
|
|
|
+ origin: [1, 1].into(),
|
|
|
+ size: [2, 2].into(),
|
|
|
+ }).expect("Did not find expected BoardWindowIterMut");
|
|
|
+ for (x, y, v) in iter {
|
|
|
+ *v = -(2 * x as isize + 2 * y as isize);
|
|
|
+ }
|
|
|
+
|
|
|
+ assert_eq!(b, board_from_vec![
|
|
|
+ 4,4;
|
|
|
+ [ 1, 2, 3, 4,
|
|
|
+ 5,-4,-6, 8,
|
|
|
+ 8,-6,-8, 5,
|
|
|
+ 4, 3, 2, 1,
|
|
|
+ ]
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+}
|