diff --git a/mino/src/matrix.rs b/mino/src/matrix.rs index 5164570..b2dc29a 100644 --- a/mino/src/matrix.rs +++ b/mino/src/matrix.rs @@ -178,12 +178,8 @@ where data[..self.rows].copy_from_slice(mat.data()); } - /// Fills in the cell at the given (x,y) coordinate. Adds new rows to the top of the - /// matrix if necessary. - /// - /// Panics if the buffer space cannot fit the new rows. - pub fn set(&mut self, x: i16, y: i16) { - if y < 0 || !(0..COLUMNS).contains(&x) { + pub fn fill_row(&mut self, y: i16, mask: u16) { + if y < 0 { // OOB coordinates are considered already set return; } @@ -195,7 +191,18 @@ where .expect("y should be within available buffer space") = EMPTY_ROW; self.rows += 1; } - data[y] |= 1 << x; + data[y] |= mask; + } + + /// Fills in the cell at the given (x,y) coordinate. Adds new rows to the top of the + /// matrix if necessary. + /// + /// Panics if the buffer space cannot fit the new rows. + #[inline] + pub fn set(&mut self, x: i16, y: i16) { + if (0..COLUMNS).contains(&x) { + self.fill_row(y, 1 << x); + } } /// Removes any rows that are completely filled, shifting rows above down. Returns a @@ -378,10 +385,37 @@ mod test { assert_eq!(buf, mat); } + #[test] + fn test_fill() { + let mut buf: MatBuf<[u16; 5]> = MatBuf::new(); + buf.fill_row(1, 0b110111); // a + let mat = mat! { + "aaa.aa...."; + ".........."; + }; + assert_eq!(buf, mat); + buf.fill_row(3, 0b1000000000); // b + buf.fill_row(0, u16::MAX); // c + let mat = mat! { + ".........b"; + ".........."; + "aaa.aa...."; + "cccccccccc"; + }; + assert_eq!(buf, mat); + } + #[test] #[should_panic] fn test_set_oob() { let mut buf: MatBuf<[u16; 4]> = MatBuf::new(); buf.set(0, 4); } + + #[test] + #[should_panic] + fn test_fill_oob() { + let mut buf: MatBuf<[u16; 4]> = MatBuf::new(); + buf.fill_row(4, 0b1001); + } } diff --git a/mino/src/piece.rs b/mino/src/piece.rs index da61dd4..6fe1d5d 100644 --- a/mino/src/piece.rs +++ b/mino/src/piece.rs @@ -1,6 +1,6 @@ //! Data structures for representing pieces and shapes. -use crate::matrix::Mat; +use crate::matrix::{Mat, MatBuf}; use core::ops::Range; @@ -150,6 +150,23 @@ impl<'c> Cells<'c> { false } + /// Fills in all of the cells onto the given matrix. Panics if the piece is out of + /// bounds. + pub fn fill(&self, mat: &mut MatBuf) + where + T: AsRef<[u16]> + AsMut<[u16]>, + { + assert!( + self.x >= 0 && self.x + self.w <= mat.cols() && self.y >= 0, + "fill() cells oob" + ); + for (dy, &row) in self.data().iter().enumerate() { + let y = self.y + dy as i16; + let mask = row << self.x; // should never overflow + mat.fill_row(y, mask); + } + } + /// Translates the cells by the given amount in both directions. #[inline] pub fn translate(&self, dx: i16, dy: i16) -> Self { @@ -330,4 +347,45 @@ pub mod test { assert_eq!(isects(1..=9, 2, W), [__, __, __, __, __, __, __, __, xx]); assert_eq!(isects(1..=9, 3, W), [__; 9]); } + + fn filled(locs: I) -> MatBuf + where + I: IntoIterator, + Loc: From, + { + let mut mat = MatBuf::new(); + for loc in locs { + let piece = Piece { + ty: Tri, + loc: loc.into(), + }; + piece.cells().fill(&mut mat); + } + mat + } + + #[test] + fn test_fill() { + let act = filled([(0, 0)]); + let exp = mat! { + "x........."; + "xx........"; + }; + assert_eq!(act, exp); + + let act = filled([(1, 1, Rot::N), (5, 0, Rot::W)]); + let exp = mat! { + ".n........"; + ".nn..w...."; + "....ww...."; + }; + assert_eq!(act, exp); + + let act = filled([(1, 1, Rot::S), (1, 1, Rot::E)]); + let exp = mat! { + "sxe......."; + ".x........"; + }; + assert_eq!(act, exp); + } }