pinch_points/sim/
direction.rs1#[derive(Clone, Copy, PartialEq, Eq, Debug)]
5pub enum Direction {
6 Up,
7 Right,
8 Down,
9 Left,
10}
11
12impl Direction {
13 pub const ALL: [Direction; 4] = [
16 Direction::Up,
17 Direction::Right,
18 Direction::Down,
19 Direction::Left,
20 ];
21
22 pub fn letter(self) -> char {
25 match self {
26 Direction::Up => 'U',
27 Direction::Down => 'D',
28 Direction::Left => 'L',
29 Direction::Right => 'R',
30 }
31 }
32
33 pub fn from_letter(letter: &str) -> Option<Direction> {
34 match letter {
35 "U" => Some(Direction::Up),
36 "D" => Some(Direction::Down),
37 "L" => Some(Direction::Left),
38 "R" => Some(Direction::Right),
39 _ => None,
40 }
41 }
42
43 pub fn left(self) -> Self {
45 match self {
46 Direction::Up => Direction::Left,
47 Direction::Left => Direction::Down,
48 Direction::Down => Direction::Right,
49 Direction::Right => Direction::Up,
50 }
51 }
52
53 pub fn right(self) -> Self {
55 match self {
56 Direction::Up => Direction::Right,
57 Direction::Right => Direction::Down,
58 Direction::Down => Direction::Left,
59 Direction::Left => Direction::Up,
60 }
61 }
62
63 pub fn reverse(self) -> Self {
64 match self {
65 Direction::Up => Direction::Down,
66 Direction::Down => Direction::Up,
67 Direction::Left => Direction::Right,
68 Direction::Right => Direction::Left,
69 }
70 }
71
72 pub fn offset(self) -> (i32, i32) {
74 match self {
75 Direction::Up => (0, -1),
76 Direction::Down => (0, 1),
77 Direction::Left => (-1, 0),
78 Direction::Right => (1, 0),
79 }
80 }
81
82 pub(crate) fn id(self) -> u8 {
83 match self {
84 Direction::Up => 0,
85 Direction::Right => 1,
86 Direction::Down => 2,
87 Direction::Left => 3,
88 }
89 }
90
91 pub(crate) fn from_id(id: u8) -> Direction {
94 match id & 0b11 {
95 0 => Direction::Up,
96 1 => Direction::Right,
97 2 => Direction::Down,
98 _ => Direction::Left,
99 }
100 }
101
102 pub fn toward(dx: i32, dy: i32) -> Direction {
106 if dx.abs() >= dy.abs() {
107 if dx > 0 {
108 Direction::Right
109 } else {
110 Direction::Left
111 }
112 } else if dy > 0 {
113 Direction::Down
114 } else {
115 Direction::Up
116 }
117 }
118}
119
120#[cfg(test)]
121mod tests {
122 use super::Direction;
123 use super::Direction::*;
124
125 #[test]
126 fn letters_round_trip_for_every_direction() {
127 for dir in [Up, Down, Left, Right] {
128 assert_eq!(Direction::from_letter(&dir.letter().to_string()), Some(dir));
129 }
130 assert_eq!(Direction::from_letter("X"), None);
131 }
132
133 #[test]
134 fn left_right_reverse_are_consistent() {
135 for d in [Up, Right, Down, Left] {
136 assert_eq!(d.left().right(), d);
137 assert_eq!(d.right().left(), d);
138 assert_eq!(d.left().left(), d.reverse());
139 assert_eq!(d.right().right(), d.reverse());
140 assert_eq!(d.reverse().reverse(), d);
141 }
142 }
143
144 #[test]
145 fn id_round_trips() {
146 for d in [Up, Right, Down, Left] {
147 assert_eq!(Direction::from_id(d.id()), d);
148 }
149 for byte in 0..=u8::MAX {
151 let _ = Direction::from_id(byte);
152 }
153 }
154
155 #[test]
156 fn toward_picks_the_dominant_axis() {
157 assert_eq!(Direction::toward(5, 2), Right);
158 assert_eq!(Direction::toward(-5, 2), Left);
159 assert_eq!(Direction::toward(1, 4), Down);
160 assert_eq!(Direction::toward(1, -4), Up);
161 assert_eq!(Direction::toward(3, 3), Right); assert_eq!(Direction::toward(-3, -3), Left);
163 }
164
165 #[test]
166 fn screen_space_handedness() {
167 assert_eq!(Up.left(), Left);
169 assert_eq!(Down.left(), Right);
170 }
171}