From 0597b3b538515ce2f21be8f377ceca19f838f5bc Mon Sep 17 00:00:00 2001 From: tali Date: Tue, 13 Dec 2022 14:11:09 -0500 Subject: [PATCH] Add Location, Orientation, Rotation types --- mino/src/lib.rs | 2 + mino/src/location.rs | 110 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 mino/src/location.rs diff --git a/mino/src/lib.rs b/mino/src/lib.rs index 61f13b5..fac5e43 100644 --- a/mino/src/lib.rs +++ b/mino/src/lib.rs @@ -1,7 +1,9 @@ #![no_std] +pub mod location; pub mod matrix; +pub use location::Location; pub use matrix::Mat; /// Allows constructing a `Mat` constant with string literals: diff --git a/mino/src/location.rs b/mino/src/location.rs new file mode 100644 index 0000000..df03dd5 --- /dev/null +++ b/mino/src/location.rs @@ -0,0 +1,110 @@ +//! Data structures and operations for representing piece locations and rotations. + +/// Represents a location for a piece, including its orientation. +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct Location { + /// Horizontal coordinate. + pub x: i16, + /// Vertical coordinate. + pub y: i16, + /// Orientation. + pub r: Orientation, +} + +/// Represents an orientation state for a piece. The initial state is "north" (`N`), and +/// there are 4 total orientations to represent each 90 degree turn possible. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Default)] +#[repr(i8)] +pub enum Orientation { + /// North. The initial state. + #[default] + N = 0, + /// Clockwise ("cw") from north. + E = 1, + /// Two rotations in any direction from north. + S = 2, + /// Counterclockwise ("ccw") from north. + W = 3, +} + +impl From for Orientation { + #[inline] + fn from(v: i8) -> Self { + unsafe { core::mem::transmute(v & 3) } + } +} + +/// Represents a rotation operation. Includes 180 degree "flips", which are non-standard +/// for tetris but exist in many unofficial iterations of the game. +/// +/// Rotations may be combined with orientations using the [addition +/// operator](core::ops::Add). +/// +/// ``` +/// # use mino::location::{Orientation, Rotation}; +/// assert_eq!(Orientation::N + Rotation::Cw, Orientation::E); +/// ``` +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug)] +#[repr(i8)] +pub enum Rotation { + /// Clockwise. + Cw = 1, + /// 180 degrees. + Flip = 2, + /// Counterclockwise. + Ccw = 3, +} + +impl core::ops::Add for Orientation { + type Output = Orientation; + fn add(self, r: Rotation) -> Orientation { + (self as i8).wrapping_add(r as i8).into() + } +} + +impl core::ops::AddAssign for Orientation { + fn add_assign(&mut self, r: Rotation) { + *self = *self + r; + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_angle_rotate() { + use Orientation::*; + use Rotation::*; + assert_eq!(N + Cw, E); + assert_eq!(N + Ccw, W); + assert_eq!(N + Flip, S); + assert_eq!(E + Cw, S); + assert_eq!(E + Ccw, N); + assert_eq!(E + Flip, W); + assert_eq!(S + Cw, W); + assert_eq!(S + Ccw, E); + assert_eq!(S + Flip, N); + assert_eq!(W + Cw, N); + assert_eq!(W + Ccw, S); + assert_eq!(W + Flip, E); + + let mut r = N; + r += Ccw; + assert_eq!(r, W); + } + + #[test] + fn test_angle_from_i8() { + let mut r = Orientation::N; + for i in 0i8..=127 { + assert_eq!(r, i.into(), "i={i}"); + r += Rotation::Cw; + } + r = Orientation::N; + for i in (-128i8..=-1).rev() { + r += Rotation::Ccw; + assert_eq!(r, i.into(), "i={i}"); + } + } +}