#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Direction {
Up,
Right,
Down,
Left,
}
impl Direction {
pub const ALL: [Direction; 4] = [
Direction::Up,
Direction::Right,
Direction::Down,
Direction::Left,
];
pub fn letter(self) -> char {
match self {
Direction::Up => 'U',
Direction::Down => 'D',
Direction::Left => 'L',
Direction::Right => 'R',
}
}
pub fn from_letter(letter: &str) -> Option<Direction> {
match letter {
"U" => Some(Direction::Up),
"D" => Some(Direction::Down),
"L" => Some(Direction::Left),
"R" => Some(Direction::Right),
_ => None,
}
}
pub fn left(self) -> Self {
match self {
Direction::Up => Direction::Left,
Direction::Left => Direction::Down,
Direction::Down => Direction::Right,
Direction::Right => Direction::Up,
}
}
pub fn right(self) -> Self {
match self {
Direction::Up => Direction::Right,
Direction::Right => Direction::Down,
Direction::Down => Direction::Left,
Direction::Left => Direction::Up,
}
}
pub fn reverse(self) -> Self {
match self {
Direction::Up => Direction::Down,
Direction::Down => Direction::Up,
Direction::Left => Direction::Right,
Direction::Right => Direction::Left,
}
}
pub fn offset(self) -> (i32, i32) {
match self {
Direction::Up => (0, -1),
Direction::Down => (0, 1),
Direction::Left => (-1, 0),
Direction::Right => (1, 0),
}
}
pub(crate) fn id(self) -> u8 {
match self {
Direction::Up => 0,
Direction::Right => 1,
Direction::Down => 2,
Direction::Left => 3,
}
}
pub(crate) fn from_id(id: u8) -> Direction {
match id & 0b11 {
0 => Direction::Up,
1 => Direction::Right,
2 => Direction::Down,
_ => Direction::Left,
}
}
pub fn toward(dx: i32, dy: i32) -> Direction {
if dx.abs() >= dy.abs() {
if dx > 0 {
Direction::Right
} else {
Direction::Left
}
} else if dy > 0 {
Direction::Down
} else {
Direction::Up
}
}
}
#[cfg(test)]
mod tests {
use super::Direction;
use super::Direction::*;
#[test]
fn letters_round_trip_for_every_direction() {
for dir in [Up, Down, Left, Right] {
assert_eq!(Direction::from_letter(&dir.letter().to_string()), Some(dir));
}
assert_eq!(Direction::from_letter("X"), None);
}
#[test]
fn left_right_reverse_are_consistent() {
for d in [Up, Right, Down, Left] {
assert_eq!(d.left().right(), d);
assert_eq!(d.right().left(), d);
assert_eq!(d.left().left(), d.reverse());
assert_eq!(d.right().right(), d.reverse());
assert_eq!(d.reverse().reverse(), d);
}
}
#[test]
fn id_round_trips() {
for d in [Up, Right, Down, Left] {
assert_eq!(Direction::from_id(d.id()), d);
}
for byte in 0..=u8::MAX {
let _ = Direction::from_id(byte);
}
}
#[test]
fn toward_picks_the_dominant_axis() {
assert_eq!(Direction::toward(5, 2), Right);
assert_eq!(Direction::toward(-5, 2), Left);
assert_eq!(Direction::toward(1, 4), Down);
assert_eq!(Direction::toward(1, -4), Up);
assert_eq!(Direction::toward(3, 3), Right); assert_eq!(Direction::toward(-3, -3), Left);
}
#[test]
fn screen_space_handedness() {
assert_eq!(Up.left(), Left);
assert_eq!(Down.left(), Right);
}
}