Refactor: change how shapes are represented.

main
Elijah Voigt 1 month ago
parent 882aac6e6e
commit 50c802fdab

@ -264,7 +264,7 @@ struct ShapeStore(Option<Shape>);
impl Display for ShapeStore { impl Display for ShapeStore {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 { match &self.0 {
Some(inner) => write!(f, "{}", inner.as_ascii()), Some(inner) => write!(f, "{}", inner.as_ascii()),
None => write!(f, "---"), None => write!(f, "---"),
} }
@ -572,10 +572,9 @@ fn init_debug_ui(mut commands: Commands) {
}); });
} }
#[derive(Component, Debug, Clone, Copy)] #[derive(Component, Debug, Clone)]
enum Shape { struct Shape {
M4(Mat4), layout: ShapeBlockLayout,
M3(Mat3),
} }
impl Default for Shape { impl Default for Shape {
@ -586,176 +585,160 @@ impl Default for Shape {
impl Display for Shape { impl Display for Shape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_ascii()) write!(f, "{}", self.layout.as_ascii())
} }
} }
impl Shape { impl From<Vec<Vec<u8>>> for Shape {
fn from_mat4(input: Mat4) -> Self { fn from(inner: Vec<Vec<u8>>) -> Shape {
Self::M4(input) Shape {
} layout: ShapeBlockLayout { inner },
}
fn from_mat3(input: Mat3) -> Self {
Self::M3(input)
} }
}
impl Shape {
fn new_o() -> Self { fn new_o() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![1, 1], vec![1, 1]].into()
[0., 0., 0., 0.],
[0., 1., 1., 0.],
[0., 1., 1., 0.],
[0., 0., 0., 0.],
]))
} }
fn new_t() -> Self { fn new_t() -> Self {
Self::from_mat3(Mat3::from_cols_array_2d(&[ vec![
[0., 1., 0.], vec![0, 1, 0],
[1., 1., 1.], vec![1, 1, 1],
[0., 0., 0.], vec![0, 0, 0]
])) ].into()
} }
fn new_l() -> Self { fn new_l() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![1, 0], vec![1, 0], vec![1, 1]].into()
[0., 0., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 0., 0.],
[0., 1., 1., 0.],
]))
} }
fn new_j() -> Self { fn new_j() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![0, 1], vec![0, 1], vec![1, 1]].into()
[0., 0., 0., 0.],
[0., 0., 1., 0.],
[0., 0., 1., 0.],
[0., 1., 1., 0.],
]))
} }
fn new_s() -> Self { fn new_s() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![0, 1, 1], vec![1, 1, 0]].into()
[0., 0., 0., 0.],
[0., 1., 1., 0.],
[1., 1., 0., 0.],
[0., 0., 0., 0.],
]))
} }
fn new_z() -> Self { fn new_z() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![1, 1, 0], vec![0, 1, 1]].into()
[0., 0., 0., 0.],
[1., 1., 0., 0.],
[0., 1., 1., 0.],
[0., 0., 0., 0.],
]))
} }
fn new_i() -> Self { fn new_i() -> Self {
Self::from_mat4(Mat4::from_cols_array_2d(&[ vec![vec![1], vec![1], vec![1], vec![1]].into()
[0., 0., 1., 0.],
[0., 0., 1., 0.],
[0., 0., 1., 0.],
[0., 0., 1., 0.],
]))
} }
// Rotates 90 degrees to the right // Rotates 90 degrees to the right
// https://stackoverflow.com/a/8664879 // https://stackoverflow.com/a/8664879
fn rotated(&self) -> Self { fn rotated(&self) -> Self {
match self { Self {
Self::M4(inner) => { layout: self.layout.rotated(),
let mut new_self = inner.transpose();
for i in 0..4 {
let col = new_self.col_mut(i);
*col = Vec4::new(col[3], col[2], col[1], col[0]);
}
Self::M4(new_self)
}
Self::M3(inner) => {
let mut new_self = inner.transpose();
for i in 0..3 {
let col = new_self.col_mut(i);
*col = Vec3::new(col[2], col[1], col[0]);
}
Self::M3(new_self)
}
} }
} }
fn reposition(
(x_offset, y_offset): (isize, isize),
center: &GridPosition,
) -> Result<GridPosition, OutOfBoundsError> {
center.with_offset(x_offset, y_offset)
}
fn coordinates( fn coordinates(
&self, &self,
center: &GridPosition, center: &GridPosition,
) -> impl Iterator<Item = Result<GridPosition, OutOfBoundsError>> { ) -> impl Iterator<Item = Result<GridPosition, OutOfBoundsError>> {
let mut v: Vec<Result<GridPosition, OutOfBoundsError>> = Vec::new(); self.layout
match self { .coordinates()
Self::M4(inner) => { .map(|(x, y)| Shape::reposition((x, y), center))
for (i, y) in (-1..3).rev().enumerate() {
let c = inner.col(i);
for (j, x) in (-1..3).enumerate() {
if c[j] == 1.0 {
v.push(center.with_offset(x, y));
}
}
}
}
Self::M3(inner) => {
for (i, y) in (-1..2).rev().enumerate() {
let c = inner.col(i);
for (j, x) in (-1..2).enumerate() {
if c[j] == 1.0 {
v.push(center.with_offset(x, y));
}
}
}
}
};
v.into_iter()
} }
fn as_ascii(&self) -> String { fn as_ascii(&self) -> String {
let mut output = String::default(); self.layout.as_ascii()
}
match self { fn height(&self) -> usize {
Self::M4(this) => { self.layout.height()
for i in 0..4 { }
let col = this.col(i).to_array(); }
output += format!("{}{}{}{}\n", col[0], col[1], col[2], col[3]).as_str();
} #[derive(Debug, Clone, PartialEq, Eq)]
} struct ShapeBlockLayout {
Self::M3(this) => { inner: Vec<Vec<u8>>,
for i in 0..3 { }
let col = this.col(i).to_array();
output += format!("{}{}{}\n", col[0], col[1], col[2]).as_str(); impl ShapeBlockLayout {
} fn rotated(&self) -> Self {
let mut inner = vec![];
for _ in 0..self.inner[0].len() {
inner.push(vec![]);
}
for y in self.inner.iter() {
for (j, x) in y.iter().enumerate() {
inner[j].insert(0, *x);
} }
}
ShapeBlockLayout { inner }
}
fn center(&self) -> (usize, usize) {
let mid_x = match self.inner[0].len() % 2 {
0 => (self.inner[0].len() - 1) / 2,
1 => self.inner[0].len() / 2,
_ => panic!("That's not how mod works!"),
};
let mid_y = match self.inner.len() % 2 {
// If we have an even number of elements
0 => self.inner.len().div_ceil(2),
// If we have an odd number of elements
1 => self.inner.len() / 2,
_ => panic!("That's not how mod works!"),
}; };
output (mid_x, mid_y)
} }
fn height(&self) -> usize { fn coordinates(&self) -> impl Iterator<Item = (isize, isize)> {
let mut x = 0; let (mid_x, mid_y) = self.center();
// Loop over outer vec (i)
self.inner
.iter()
.rev()
.enumerate()
.flat_map(move |(i, ys)| {
ys.iter().enumerate().filter_map(move |(j, val)| {
// this_x = j - mid_x
let x = j as isize - mid_x as isize;
// this_y = i - mid_y
let y = i as isize - mid_y as isize;
(*val > 0).then_some((x, y))
})
})
}
match self { // TODO: Make fnctional with chaining
Self::M4(this) => { fn as_ascii(&self) -> String {
for i in 0..4 { let mut s = String::new();
if this.col(i).to_array().contains(&1.0) { for y in self.inner.iter() {
x += 1 for x in y {
} match x {
} 0 => s.push('0'),
} 1 => s.push('1'),
Self::M3(this) => { _ => panic!("aur nau"),
for i in 0..3 {
if this.col(i).to_array().contains(&1.0) {
x += 1
}
} }
} }
}; s.push('\n');
}
s
}
x fn height(&self) -> usize {
self.inner.len()
} }
} }
@ -1056,7 +1039,7 @@ fn movement(
MovementDirection::Down MovementDirection::Down
| MovementDirection::Left | MovementDirection::Left
| MovementDirection::Right | MovementDirection::Right
| MovementDirection::Skip => *this_shape, | MovementDirection::Skip => this_shape.clone(),
MovementDirection::Rotate => this_shape.rotated(), MovementDirection::Rotate => this_shape.rotated(),
}; };
debug!( debug!(
@ -1105,7 +1088,7 @@ fn movement(
// Update shape/rotation // Update shape/rotation
let mut s = shape.get_mut(event.entity).unwrap(); let mut s = shape.get_mut(event.entity).unwrap();
*s = new_shape; *s = new_shape.clone();
} }
} }
} }
@ -1125,7 +1108,7 @@ fn swap(
None => { None => {
// Copy current shape into store // Copy current shape into store
// De-activate entity // De-activate entity
store.0 = Some(*shapes.get(event.entity).unwrap()); store.0 = Some(shapes.get(event.entity).unwrap().clone());
commands commands
.entity(event.entity) .entity(event.entity)
.despawn_related::<ShapeBlocks>() .despawn_related::<ShapeBlocks>()

@ -35,25 +35,19 @@ fn test_shape_t() {
fn test_shape_i() { fn test_shape_i() {
let mut shape = Shape::new_i(); let mut shape = Shape::new_i();
let expected_up = "0010\n\ let expected_up = "1\n\
0010\n\ 1\n\
0010\n\ 1\n\
0010\n"; 1\n";
let expected_right = "0000\n\ let expected_right = "1111\n";
0000\n\
1111\n\ let expected_down = "1\n\
0000\n"; 1\n\
1\n\
let expected_down = "0100\n\ 1\n";
0100\n\
0100\n\ let expected_left = "1111\n";
0100\n";
let expected_left = "0000\n\
1111\n\
0000\n\
0000\n";
assert_eq!(shape.as_ascii(), expected_up); assert_eq!(shape.as_ascii(), expected_up);
shape = shape.rotated(); shape = shape.rotated();
@ -72,21 +66,155 @@ fn test_coordinates() {
let center = GridPosition { x: 5, y: 5 }; let center = GridPosition { x: 5, y: 5 };
let expected: Vec<Result<GridPosition, GameError>> = vec![ let actual: Vec<Result<GridPosition, OutOfBoundsError>> = shape.coordinates(&center).collect();
Ok((5, 6).into()),
let expected: Vec<Result<GridPosition, OutOfBoundsError>> = vec![
Ok((4, 5).into()), Ok((4, 5).into()),
Ok((5, 5).into()), Ok((5, 5).into()),
Ok((6, 5).into()), Ok((6, 5).into()),
Ok((5, 6).into()),
]; ];
let actual: Vec<Result<GridPosition, GameError>> = shape.coordinates(&center).collect(); assert_eq!(shape.layout.center(), (1, 1));
assert_eq!(actual, expected); assert_eq!(actual, expected);
} }
#[test] #[test]
fn test_height() { fn test_height() {
assert_eq!(Shape::new_t().height(), 2); // todo: should be 2 for t piece
assert_eq!(Shape::new_t().height(), 3);
assert_eq!(Shape::new_i().height(), 4); assert_eq!(Shape::new_i().height(), 4);
assert_eq!(Shape::new_l().height(), 3); assert_eq!(Shape::new_l().height(), 3);
} }
#[test]
fn test_shape_block_layout_rotation() {
{
let actual = ShapeBlockLayout {
inner: vec![vec![0, 0, 1]],
}
.rotated();
let expected = ShapeBlockLayout {
inner: vec![vec![0], vec![0], vec![1]],
};
assert_eq!(expected, actual);
}
{
let actual = ShapeBlockLayout {
inner: vec![vec![1, 2, 3], vec![4, 5, 6]],
}
.rotated();
let expected = ShapeBlockLayout {
inner: vec![vec![4, 1], vec![5, 2], vec![6, 3]],
};
assert_eq!(expected, actual);
}
{
let actual = ShapeBlockLayout {
inner: vec![vec![1, 2, 3], vec![4, 5, 6]],
}
.rotated();
let expected = ShapeBlockLayout {
inner: vec![vec![4, 1], vec![5, 2], vec![6, 3]],
};
assert_eq!(expected, actual);
}
{
let actual = ShapeBlockLayout {
inner: vec![
vec![1, 2, 3, 4],
vec![5, 6, 7, 8],
vec![9, 10, 11, 12],
vec![13, 14, 15, 16],
],
}
.rotated();
let expected = ShapeBlockLayout {
inner: vec![
vec![13, 9, 5, 1],
vec![14, 10, 6, 2],
vec![15, 11, 7, 3],
vec![16, 12, 8, 4],
],
};
assert_eq!(expected, actual);
}
}
#[test]
fn test_shape_block_center() {
{
let actual = ShapeBlockLayout {
inner: vec![
vec![0],
vec![0],
vec![0],
]
}.center();
let expected = (0, 1);
assert_eq!(actual, expected);
}
{
let actual = ShapeBlockLayout {
inner: vec![
vec![0],
vec![0],
vec![0],
vec![0],
]
}.center();
let expected = (0, 2);
assert_eq!(actual, expected);
}
{
let actual = ShapeBlockLayout {
inner: vec![
vec![0, 0],
vec![0, 0],
]
}.center();
let expected = (0, 1);
assert_eq!(actual, expected);
}
{
let actual = ShapeBlockLayout {
inner: vec![
vec![0, 0, 0],
vec![0, 0, 0],
vec![0, 0, 0],
]
}.center();
let expected = (1, 1);
assert_eq!(actual, expected);
}
{
let actual = ShapeBlockLayout {
inner: vec![
vec![0, 0, 0, 0],
vec![0, 0, 0, 0],
vec![0, 0, 0, 0],
vec![0, 0, 0, 0],
]
}.center();
let expected = (1, 2);
assert_eq!(actual, expected);
}
}

Loading…
Cancel
Save