Browse Source

Move some things around and add a Rect abstraction

Getty Ritter 4 years ago
parent
commit
a9080c3c4b
3 changed files with 467 additions and 142 deletions
  1. 376 0
      carpet/src/board.rs
  2. 5 142
      carpet/src/lib.rs
  3. 86 0
      carpet/src/types.rs

+ 376 - 0
carpet/src/board.rs

@@ -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,
+            ]
+        ]);
+    }
+
+}

+ 5 - 142
carpet/src/lib.rs

@@ -7,6 +7,11 @@ use std::path::Path;
 pub use winit::VirtualKeyCode;
 pub use ggez::input::keyboard::KeyMods;
 
+mod board;
+mod types;
+pub use board::{Board, BoardIter};
+pub use types::{Coord, Rect, Size};
+
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
 pub enum Color {
     Red,
@@ -114,30 +119,6 @@ impl<Idx: Tile> Tileset<Idx> {
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub struct Size {
-    width: usize,
-    height: usize,
-}
-
-impl From<[usize; 2]> for Size {
-    fn from([width, height]: [usize; 2]) -> Size {
-        Size { width, height }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub struct Coord {
-    x: usize,
-    y: usize,
-}
-
-impl From<[usize; 2]> for Coord {
-    fn from([x, y]: [usize; 2]) -> Coord {
-        Coord { x, y }
-    }
-}
-
 impl<Idx: Tile> GameBoard<Idx> {
     pub fn new(size: impl Into<Size>, mut tileset: Tileset<Idx>) -> GameBoard<Idx> {
         let size = size.into();
@@ -451,121 +432,3 @@ impl<Idx: Tile + 'static> GameBuilder<Idx> {
         Game::create(board, ctx, evloop)
     }
 }
-
-
-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 }
-    }
-
-    pub fn get(&self, x: usize, y: usize) -> Option<&T> {
-        let idx = x + self.width * y;
-        self.storage.get(idx)
-    }
-
-    pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
-        let idx = x + self.width * y;
-        self.storage.get_mut(idx)
-    }
-
-    pub fn set(&mut self, x: usize, y: usize, val: T) {
-        let idx = x + self.width * y;
-        self.storage[idx] = val;
-    }
-
-    pub fn width(&self) -> usize {
-        self.width
-    }
-
-    pub fn height(&self) -> usize {
-        self.height
-    }
-
-    pub fn contains(&self, pos: impl Into<Coord>) -> bool {
-        let pos = pos.into();
-        pos.x < self.width && pos.y < self.height
-    }
-
-    pub fn iter(&self) -> BoardIter<T> {
-        BoardIter {
-            n: 0,
-            board: &self,
-        }
-    }
-}
-
-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)))
-    }
-}
-
-
-
-pub struct BoardIter<'a, T> {
-    n: usize,
-    board: &'a Board<T>,
-}
-
-impl<'a, T> Iterator for BoardIter<'a, T> {
-    type Item = (usize, usize, &'a T);
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.n < self.board.storage.len() {
-            let x = self.n % self.board.width;
-            let y = self.n / self.board.width;
-            self.n += 1;
-            Some((x, y, &self.board.storage[self.n-1]))
-        } else {
-            None
-        }
-    }
-}

+ 86 - 0
carpet/src/types.rs

@@ -0,0 +1,86 @@
+use std::cmp::{min, max};
+
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub struct Size {
+    pub width: usize,
+    pub height: usize,
+}
+
+impl specs::Component for Size {
+    type Storage = specs::VecStorage<Self>;
+}
+
+impl From<[usize; 2]> for Size {
+    fn from([width, height]: [usize; 2]) -> Size {
+        Size { width, height }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub struct Coord {
+    pub x: usize,
+    pub y: usize,
+}
+
+impl specs::Component for Coord {
+    type Storage = specs::VecStorage<Coord>;
+}
+
+impl From<[usize; 2]> for Coord {
+    fn from([x, y]: [usize; 2]) -> Coord {
+        Coord { x, y }
+    }
+}
+
+
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub struct Rect {
+    pub origin: Coord,
+    pub size: Size,
+}
+
+impl Rect {
+    pub fn new(origin: Coord, size: Size) -> Rect{
+        Rect { origin, size }
+    }
+
+    pub fn from_points(p1: Coord, p2: Coord) -> Rect {
+        let origin = Coord {
+            x: min(p1.x, p2.x),
+            y: min(p1.y, p2.y),
+        };
+        let size = Size {
+            width: max(p1.x, p2.x) - origin.x,
+            height: max(p1.y, p2.y) - origin.y,
+        };
+        Rect { origin, size }
+    }
+
+    pub fn area(&self) -> usize {
+        self.size.width * self.size.height
+    }
+
+    pub fn width(&self) -> usize {
+        self.size.width
+    }
+
+    pub fn height(&self) -> usize {
+        self.size.height
+    }
+
+    pub fn x(&self) -> usize {
+        self.origin.x
+    }
+
+    pub fn y(&self) -> usize {
+        self.origin.y
+    }
+
+    pub fn contains(&self, pt: impl Into<Coord>) -> bool {
+        let pt = pt.into();
+        pt.x >= self.origin.x &&
+            pt.y >= self.origin.y &&
+            pt.x < self.origin.x + self.size.width &&
+            pt.y < self.origin.y + self.size.height
+    }
+}