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 { width: usize, height: usize, storage: Vec, } impl Board { pub fn new_with_default(width: usize, height: usize, default: T) -> Board { let mut storage = Vec::with_capacity(width * height); for _ in 0..width * height { storage.push(default.clone()) } Board { width, height, storage, } } } impl Board { pub fn new_from( width: usize, height: usize, mut func: impl FnMut(usize, usize) -> T, ) -> Board { 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) -> 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; 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 { 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 { BoardIterMut { n: 0, width: self.width, iter: self.storage.iter_mut(), } } pub fn window_iter(&self, window: Rect) -> Option> { 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> { if !self.contains_rect(window) { return None; } Some(BoardWindowIterMut { n: 0, width: self.width, window, iter: self.storage.iter_mut(), }) } } impl std::ops::Index for Board { 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 std::ops::Index<(usize, usize)> for Board { 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 std::ops::Index<[usize; 2]> for Board { 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 std::ops::IndexMut for Board { 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 std::ops::IndexMut<(usize, usize)> for Board { 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 std::ops::IndexMut<[usize; 2]> for Board { 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 { 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 { 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 { for v in self.iter.by_ref() { 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 { for v in self.iter.by_ref() { 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.get(x + y * w).unwrap().clone() }) } } } #[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 = 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, ] ] ); } }