123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- 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: Clone> Board<T> {
- pub fn new_with_default(width: usize, height: usize, default: T) -> Board<T> {
- let mut storage = Vec::with_capacity(width * height);
- for _ in 0..width * height {
- storage.push(default.clone())
- }
- Board {
- width,
- height,
- storage,
- }
- }
- }
- 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;
- 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> {
- 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<Self::Item> {
- 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<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,
- ]
- ]
- );
- }
- }
|