shrimplify: MatBuf is only generic of its length, not buffer type
This commit is contained in:
parent
15d69fe128
commit
d564bc25af
|
@ -2,6 +2,9 @@
|
|||
//! uncolored, since color information is generally not used by bots and many operations
|
||||
//! can be made much more efficient by not tracking it.
|
||||
|
||||
use core::cmp::{max, min};
|
||||
use core::ops::{Range, RangeFull};
|
||||
|
||||
/// Number of columns in a matrix.
|
||||
pub const COLUMNS: i16 = 10;
|
||||
|
||||
|
@ -64,16 +67,15 @@ impl Mat {
|
|||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn to_array_range(&self, r: core::ops::Range<i16>) -> core::ops::Range<usize> {
|
||||
let y1 = core::cmp::max(r.end, 0);
|
||||
let y1 = core::cmp::min(y1 as usize, self.0.len());
|
||||
let y0 = core::cmp::max(r.start, 0);
|
||||
let y0 = core::cmp::min(y0 as usize, y1);
|
||||
y0..y1
|
||||
impl AsRef<Mat> for Mat {
|
||||
fn as_ref(&self) -> &Mat {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// `mat[y]` = bit pattern of row `y`
|
||||
impl core::ops::Index<i16> for Mat {
|
||||
type Output = u16;
|
||||
fn index(&self, y: i16) -> &u16 {
|
||||
|
@ -85,16 +87,20 @@ impl core::ops::Index<i16> for Mat {
|
|||
}
|
||||
}
|
||||
|
||||
impl core::ops::Index<core::ops::Range<i16>> for Mat {
|
||||
// `mat[y0..y1]` = bit patterns of rows in range
|
||||
impl core::ops::Index<Range<i16>> for Mat {
|
||||
type Output = [u16];
|
||||
fn index(&self, r: core::ops::Range<i16>) -> &[u16] {
|
||||
&self.0[self.to_array_range(r)]
|
||||
fn index(&self, r: Range<i16>) -> &[u16] {
|
||||
let y1 = min(max(r.end, 0), self.rows());
|
||||
let y0 = min(max(r.start, 0), y1);
|
||||
&self.0[y0 as usize..y1 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl core::ops::Index<core::ops::RangeFull> for Mat {
|
||||
// `mat[..]` = bit patterns of all rows
|
||||
impl core::ops::Index<RangeFull> for Mat {
|
||||
type Output = [u16];
|
||||
fn index(&self, _: core::ops::RangeFull) -> &[u16] {
|
||||
fn index(&self, _: RangeFull) -> &[u16] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
@ -144,164 +150,188 @@ pub mod __ascii {
|
|||
}
|
||||
}
|
||||
|
||||
/// Wrapper struct for using an underlying buffer (such as an array or vec) as a
|
||||
/// "writable" matrix. This allows operations such as changing if a cell is occupied or
|
||||
/// not.
|
||||
/// Matrix bitboard backed by a static u16 array of a given length. This type allows
|
||||
/// writable operations such as changing if a cell is occupied or not, and growing or
|
||||
/// shrinking the number of rows as long as it stays within the underlying capacity.
|
||||
///
|
||||
/// [`MatBuf`] implements [`Deref`], so it automatically inherits the methods of [`Mat`].
|
||||
#[derive(Clone, Default)]
|
||||
pub struct MatBuf<T: AsRef<[u16]> = [u16; 40]> {
|
||||
buffer: T,
|
||||
rows: usize,
|
||||
#[derive(Clone)]
|
||||
pub struct MatBuf<const LEN: usize = 40> {
|
||||
rows: i16,
|
||||
buf: [u16; LEN],
|
||||
}
|
||||
|
||||
impl<const N: usize> MatBuf<[u16; N]> {
|
||||
/// Returns a new empty [`MatBuf`] backed by a fixed-size array.
|
||||
impl MatBuf {
|
||||
/// Returns a new empty [`MatBuf`] that is able to grow up to 40 rows.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> MatBuf<LEN> {
|
||||
/// Returns a read-only view of this matrix.
|
||||
#[inline]
|
||||
pub fn as_mat(&self) -> &Mat {
|
||||
Mat::new(&self.buf[0..self.rows as usize])
|
||||
}
|
||||
|
||||
/// Modifies the cells in this matrix to be identical to those in `mat`.
|
||||
///
|
||||
/// Panics if the buffer space cannot fit the rows of `mat`.
|
||||
#[inline]
|
||||
pub fn copy_from(&mut self, mat: &Mat) {
|
||||
let data = &mat[..];
|
||||
if data.len() > LEN {
|
||||
panic!("matrix cannot fit in available buffer space");
|
||||
}
|
||||
self.rows = mat.rows();
|
||||
self.buf[..data.len()].copy_from_slice(data);
|
||||
}
|
||||
|
||||
/// Resets the matrix so it is empty.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.copy_from(Mat::EMPTY)
|
||||
}
|
||||
|
||||
/// Modifies the cells in row `y` to have additional cells occupied according to the
|
||||
/// bit pattern in `mask`.
|
||||
///
|
||||
/// Panics if the buffer space cannot fit the desired row.
|
||||
#[inline]
|
||||
pub fn fill_row(&mut self, y: i16, mask: u16) {
|
||||
if y >= 0 {
|
||||
self[y] |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills in the cell at the given (x,y) coordinate.
|
||||
///
|
||||
/// Panics if the buffer space cannot fit the desired cell.
|
||||
pub fn set(&mut self, x: i16, y: i16) {
|
||||
self.fill_row(y, if (0..COLUMNS).contains(&x) { 1 << x } else { 0 })
|
||||
}
|
||||
|
||||
/// Removes any rows that are completely filled, shifting rows above down. Returns the
|
||||
/// range of rows that were cleared.
|
||||
pub fn clear_lines(&mut self) -> Range<i16> {
|
||||
// TODO: this could be made faster if given the following assumptions:
|
||||
// - provide lowest y that may contain filled rows
|
||||
// - assume that filled rows must all be adjacent
|
||||
let mut dst = 0usize;
|
||||
let mut rng = self.rows..self.rows;
|
||||
for y in 0..self.rows {
|
||||
let i = y as usize;
|
||||
if self.buf[i] == FULL_ROW || self.buf[i] == EMPTY_ROW {
|
||||
rng.start = min(rng.start, y);
|
||||
rng.end = y + 1;
|
||||
} else {
|
||||
self.buf[dst] = self.buf[i];
|
||||
dst += 1;
|
||||
}
|
||||
}
|
||||
self.rows = dst as i16;
|
||||
rng
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> Default for MatBuf<LEN> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buffer: [0u16; N],
|
||||
buf: [0; LEN],
|
||||
rows: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MatBuf<T>
|
||||
where
|
||||
T: AsRef<[u16]>,
|
||||
{
|
||||
/// Returns the underyling buffer.
|
||||
pub fn into_inner(self) -> T {
|
||||
self.buffer
|
||||
impl<const LEN: usize> core::ops::IndexMut<i16> for MatBuf<LEN> {
|
||||
fn index_mut(&mut self, y: i16) -> &mut u16 {
|
||||
if y < 0 || y as usize >= LEN {
|
||||
panic!("row does not fit in available buffer space");
|
||||
}
|
||||
if y >= self.rows {
|
||||
self.buf[self.rows as usize..(y + 1) as usize].fill(EMPTY_ROW);
|
||||
self.rows = y + 1;
|
||||
}
|
||||
&mut self.buf[y as usize]
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a read-only view of this matrix.
|
||||
impl<const LEN: usize> core::ops::IndexMut<Range<i16>> for MatBuf<LEN> {
|
||||
fn index_mut(&mut self, r: Range<i16>) -> &mut [u16] {
|
||||
// FIXME: should this expand the buffer like `IndexMut<i16>` does?
|
||||
let y1 = min(max(r.end, 0), self.rows);
|
||||
let y0 = min(max(r.start, 0), y1);
|
||||
&mut self.buf[y0 as usize..y1 as usize]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> core::ops::IndexMut<RangeFull> for MatBuf<LEN> {
|
||||
fn index_mut(&mut self, _: RangeFull) -> &mut [u16] {
|
||||
&mut self.buf
|
||||
}
|
||||
}
|
||||
|
||||
// boilerplate impl's that all defer to `self.as_mat()`
|
||||
|
||||
impl<const LEN: usize> AsRef<Mat> for MatBuf<LEN> {
|
||||
#[inline]
|
||||
pub fn as_mat(&self) -> &Mat {
|
||||
Mat::new(&self.buffer.as_ref()[..self.rows])
|
||||
}
|
||||
|
||||
/// Resets the matrix so it is empty.
|
||||
pub fn clear(&mut self) {
|
||||
self.rows = 0;
|
||||
fn as_ref(&self) -> &Mat {
|
||||
self.as_mat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MatBuf<T>
|
||||
where
|
||||
T: AsRef<[u16]> + AsMut<[u16]>,
|
||||
{
|
||||
/// Modifies the cells in this matrix to be identical to those in `mat`.
|
||||
///
|
||||
/// Panics if the buffer space cannot fit the rows of `mat`.
|
||||
pub fn copy_from(&mut self, mat: &Mat) {
|
||||
let mat_data = &mat[..];
|
||||
let buf_data = self.buffer.as_mut();
|
||||
if mat_data.len() > buf_data.len() {
|
||||
panic!("matrix cannot fit in available buffer space");
|
||||
}
|
||||
self.rows = mat_data.len();
|
||||
buf_data[..self.rows].copy_from_slice(mat_data);
|
||||
}
|
||||
|
||||
pub fn fill_row(&mut self, y: i16, mask: u16) {
|
||||
if y < 0 {
|
||||
// OOB coordinates are considered already set
|
||||
return;
|
||||
}
|
||||
let y = y as usize;
|
||||
let buf_data = self.buffer.as_mut();
|
||||
while y >= self.rows {
|
||||
*buf_data
|
||||
.get_mut(self.rows)
|
||||
.expect("y should be within available buffer space") = EMPTY_ROW;
|
||||
self.rows += 1;
|
||||
}
|
||||
buf_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
|
||||
/// new view of the buffer that only includes the remaining rows.
|
||||
pub fn clear_lines(&mut self) {
|
||||
let data = self.buffer.as_mut();
|
||||
let mut dst_y = 0;
|
||||
for y in 0..self.rows {
|
||||
if data[y] != FULL_ROW && data[y] != EMPTY_ROW {
|
||||
data[dst_y] = data[y];
|
||||
dst_y += 1;
|
||||
}
|
||||
}
|
||||
self.rows = dst_y;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> core::ops::IndexMut<core::ops::RangeFull> for MatBuf<T>
|
||||
where
|
||||
T: AsRef<[u16]> + AsMut<[u16]>,
|
||||
{
|
||||
fn index_mut(&mut self, _: core::ops::RangeFull) -> &mut [u16] {
|
||||
&mut self.buffer.as_mut()[..self.rows]
|
||||
}
|
||||
}
|
||||
|
||||
// All boilerplate below
|
||||
|
||||
impl<T: AsRef<[u16]>> core::ops::Deref for MatBuf<T> {
|
||||
impl<const LEN: usize> core::ops::Deref for MatBuf<LEN> {
|
||||
type Target = Mat;
|
||||
fn deref(&self) -> &Mat {
|
||||
self.as_mat()
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>> core::fmt::Debug for MatBuf<T> {
|
||||
|
||||
impl<const LEN: usize, Rhs: AsRef<Mat>> core::cmp::PartialEq<Rhs> for MatBuf<LEN> {
|
||||
fn eq(&self, other: &Rhs) -> bool {
|
||||
*self.as_mat() == *other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> core::cmp::Eq for MatBuf<LEN> {}
|
||||
|
||||
impl<const LEN: usize> core::fmt::Debug for MatBuf<LEN> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
self.as_mat().fmt(f)
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>> core::cmp::Eq for MatBuf<T> {}
|
||||
impl<T: AsRef<[u16]>> core::cmp::PartialEq<Mat> for MatBuf<T> {
|
||||
fn eq(&self, other: &Mat) -> bool {
|
||||
self.as_mat() == other
|
||||
|
||||
impl<const LEN: usize> core::hash::Hash for MatBuf<LEN> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.as_mat().hash(state);
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>> core::cmp::PartialEq<&Mat> for MatBuf<T> {
|
||||
fn eq(&self, other: &&Mat) -> bool {
|
||||
self.as_mat() == *other
|
||||
|
||||
impl<const LEN: usize> core::ops::Index<i16> for MatBuf<LEN> {
|
||||
type Output = u16;
|
||||
fn index(&self, y: i16) -> &u16 {
|
||||
&self.as_mat()[y]
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>, U: AsRef<[u16]>> core::cmp::PartialEq<MatBuf<U>> for MatBuf<T> {
|
||||
fn eq(&self, other: &MatBuf<U>) -> bool {
|
||||
self.as_mat() == other.as_mat()
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>> core::ops::Index<core::ops::RangeFull> for MatBuf<T> {
|
||||
|
||||
impl<const LEN: usize> core::ops::Index<Range<i16>> for MatBuf<LEN> {
|
||||
type Output = [u16];
|
||||
fn index(&self, _: core::ops::RangeFull) -> &[u16] {
|
||||
&self.as_mat()[..]
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]> + AsMut<[u16]>> core::ops::IndexMut<core::ops::Range<i16>> for MatBuf<T> {
|
||||
fn index_mut(&mut self, r: core::ops::Range<i16>) -> &mut [u16] {
|
||||
let ar = self.to_array_range(r);
|
||||
&mut self.buffer.as_mut()[ar]
|
||||
}
|
||||
}
|
||||
impl<T: AsRef<[u16]>> core::ops::Index<core::ops::Range<i16>> for MatBuf<T> {
|
||||
type Output = [u16];
|
||||
fn index(&self, r: core::ops::Range<i16>) -> &[u16] {
|
||||
fn index(&self, r: Range<i16>) -> &[u16] {
|
||||
&self.as_mat()[r]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const LEN: usize> core::ops::Index<RangeFull> for MatBuf<LEN> {
|
||||
type Output = [u16];
|
||||
fn index(&self, _: RangeFull) -> &[u16] {
|
||||
&self.as_mat()[..]
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(?): MatVec, which is resizable
|
||||
// TODO(?): test_row(), fill_row(), clear_lines() made into traits
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -400,9 +430,26 @@ mod test {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mat_buf_size() {
|
||||
macro_rules! mat_buf_size {
|
||||
($n:literal) => {
|
||||
core::mem::size_of::<MatBuf<$n>>()
|
||||
};
|
||||
}
|
||||
assert_eq!(mat_buf_size!(0), core::mem::size_of::<[u16; 1]>());
|
||||
assert_eq!(mat_buf_size!(1), core::mem::size_of::<[u16; 2]>());
|
||||
assert_eq!(mat_buf_size!(10), core::mem::size_of::<[u16; 11]>());
|
||||
assert_eq!(mat_buf_size!(40), core::mem::size_of::<[u16; 41]>());
|
||||
assert_eq!(
|
||||
core::mem::size_of::<MatBuf>(),
|
||||
core::mem::size_of::<[u16; 41]>()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mat_buf_copy_from() {
|
||||
let mut buf: MatBuf = MatBuf::new();
|
||||
let mut buf = MatBuf::new();
|
||||
assert_eq!(buf, Mat::EMPTY);
|
||||
assert_eq!(buf.rows(), 0);
|
||||
let mat = mat! {
|
||||
|
@ -416,7 +463,7 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_lines() {
|
||||
fn test_clear_lines_1() {
|
||||
let mat0 = mat! {
|
||||
".........."; // clear
|
||||
".........."; // clear
|
||||
|
@ -431,18 +478,51 @@ mod test {
|
|||
".x.xxxxxxx";
|
||||
"x.xxxxxxxx";
|
||||
};
|
||||
let mut buf: MatBuf<[u16; 7]> = MatBuf::new();
|
||||
let mut buf: MatBuf<7> = MatBuf::default();
|
||||
assert_eq!(buf.rows(), 0);
|
||||
buf.copy_from(mat0);
|
||||
assert_eq!(buf.rows(), 7);
|
||||
buf.clear_lines();
|
||||
assert_eq!(buf.clear_lines(), 1..7);
|
||||
assert_eq!(buf, mat1);
|
||||
assert_eq!(buf.rows(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_lines_2() {
|
||||
let mut buf = MatBuf::new();
|
||||
buf.copy_from(mat! {
|
||||
".x.xxxxxxx";
|
||||
"xxxxxxxxxx"; // clear
|
||||
"xxxxxxxxxx"; // clear
|
||||
"x.xxxxxxxx";
|
||||
});
|
||||
let tgt = mat! {
|
||||
".x.xxxxxxx";
|
||||
"x.xxxxxxxx";
|
||||
};
|
||||
assert_eq!(buf.clear_lines(), 1..3);
|
||||
assert_eq!(buf, tgt);
|
||||
assert_eq!(buf.clear_lines(), 2..2);
|
||||
assert_eq!(buf, tgt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clear_lines_3() {
|
||||
let mut buf = MatBuf::new();
|
||||
buf.copy_from(mat! {
|
||||
"xxxxxxxxxx"; // clear
|
||||
"xxxxxxxxxx"; // clear
|
||||
"xxxxxxxxxx"; // clear
|
||||
});
|
||||
let tgt = Mat::EMPTY;
|
||||
assert_eq!(buf.clear_lines(), 0..3);
|
||||
assert_eq!(buf, tgt);
|
||||
assert_eq!(buf.clear_lines(), 0..0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
let mut buf: MatBuf<[u16; 4]> = MatBuf::new();
|
||||
let mut buf: MatBuf<4> = MatBuf::default();
|
||||
buf.set(0, 0); // a
|
||||
buf.set(9, 3); // b
|
||||
buf.set(1, 1); // c
|
||||
|
@ -464,7 +544,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_fill() {
|
||||
let mut buf: MatBuf<[u16; 5]> = MatBuf::new();
|
||||
let mut buf: MatBuf<5> = MatBuf::default();
|
||||
buf.fill_row(1, 0b110111); // a
|
||||
let mat = mat! {
|
||||
"aaa.aa....";
|
||||
|
@ -485,14 +565,14 @@ mod test {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn test_set_oob() {
|
||||
let mut buf: MatBuf<[u16; 4]> = MatBuf::new();
|
||||
let mut buf: MatBuf<4> = MatBuf::default();
|
||||
buf.set(0, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_fill_oob() {
|
||||
let mut buf: MatBuf<[u16; 4]> = MatBuf::new();
|
||||
let mut buf: MatBuf<4> = MatBuf::default();
|
||||
buf.fill_row(4, 0b1001);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,10 +152,7 @@ impl<'c> Cells<'c> {
|
|||
|
||||
/// Fills in all of the cells onto the given matrix. Panics if the piece is out of
|
||||
/// bounds.
|
||||
pub fn fill<T>(&self, mat: &mut MatBuf<T>)
|
||||
where
|
||||
T: AsRef<[u16]> + AsMut<[u16]>,
|
||||
{
|
||||
pub fn fill<const LEN: usize>(&self, mat: &mut MatBuf<LEN>) {
|
||||
assert!(
|
||||
self.x >= 0 && self.x + self.w <= mat.cols() && self.y >= 0,
|
||||
"fill() cells oob"
|
||||
|
|
Loading…
Reference in New Issue