1use anyhow::anyhow;
2use std::fmt;
3use std::str::FromStr;
4
5use super::{Direction, Square};
6
7#[derive(Hash, Clone, Copy, Debug, Eq, PartialEq)]
9pub enum PushPullDirection {
10 NN,
11 NE,
12 NW,
13 EN,
14 EE,
15 ES,
16 SE,
17 SS,
18 SW,
19 WN,
20 WS,
21 WW,
22}
23
24impl PushPullDirection {
25 pub fn get_steps(&self, square: Square) -> [(Square, Direction); 2] {
26 match self {
27 PushPullDirection::NN => [(square, Direction::Up), (square.shift_in_direction(Direction::Down), Direction::Up)],
28 PushPullDirection::NE => [(square, Direction::Up), (square.shift_in_direction(Direction::Left), Direction::Right)],
29 PushPullDirection::NW => [(square, Direction::Up), (square.shift_in_direction(Direction::Right), Direction::Left)],
30 PushPullDirection::EN => [(square, Direction::Right), (square.shift_in_direction(Direction::Down), Direction::Up)],
31 PushPullDirection::EE => [(square, Direction::Right), (square.shift_in_direction(Direction::Left), Direction::Right)],
32 PushPullDirection::ES => [(square, Direction::Right), (square.shift_in_direction(Direction::Up), Direction::Down)],
33 PushPullDirection::SE => [(square, Direction::Down), (square.shift_in_direction(Direction::Left), Direction::Right)],
34 PushPullDirection::SS => [(square, Direction::Down), (square.shift_in_direction(Direction::Up), Direction::Down)],
35 PushPullDirection::SW => [(square, Direction::Down), (square.shift_in_direction(Direction::Right), Direction::Left)],
36 PushPullDirection::WN => [(square, Direction::Left), (square.shift_in_direction(Direction::Down), Direction::Up)],
37 PushPullDirection::WS => [(square, Direction::Left), (square.shift_in_direction(Direction::Up), Direction::Down)],
38 PushPullDirection::WW => [(square, Direction::Left), (square.shift_in_direction(Direction::Right), Direction::Left)],
39 }
40 }
41
42 pub fn as_u8(&self) -> u8 {
43 match self {
44 PushPullDirection::NN => 0,
45 PushPullDirection::NE => 1,
46 PushPullDirection::NW => 2,
47 PushPullDirection::EN => 3,
48 PushPullDirection::EE => 4,
49 PushPullDirection::ES => 5,
50 PushPullDirection::SE => 6,
51 PushPullDirection::SS => 7,
52 PushPullDirection::SW => 8,
53 PushPullDirection::WN => 9,
54 PushPullDirection::WS => 10,
55 PushPullDirection::WW => 11,
56 }
57 }
58
59 pub fn directions() -> Vec<PushPullDirection> {
60 vec![
61 PushPullDirection::NN,
62 PushPullDirection::NE,
63 PushPullDirection::NW,
64 PushPullDirection::EN,
65 PushPullDirection::EE,
66 PushPullDirection::ES,
67 PushPullDirection::SE,
68 PushPullDirection::SS,
69 PushPullDirection::SW,
70 PushPullDirection::WN,
71 PushPullDirection::WS,
72 PushPullDirection::WW,
73 ]
74 }
75
76 pub fn is_valid_from_square(&self, square: Square) -> bool {
77 match self {
78 PushPullDirection::NN => square.can_shift_in_direction(Direction::Up) && square.can_shift_in_direction(Direction::Down),
79 PushPullDirection::NE => square.can_shift_in_direction(Direction::Up) && square.can_shift_in_direction(Direction::Left),
80 PushPullDirection::NW => square.can_shift_in_direction(Direction::Up) && square.can_shift_in_direction(Direction::Right),
81 PushPullDirection::EN => square.can_shift_in_direction(Direction::Right) && square.can_shift_in_direction(Direction::Down),
82 PushPullDirection::EE => square.can_shift_in_direction(Direction::Right) && square.can_shift_in_direction(Direction::Left),
83 PushPullDirection::ES => square.can_shift_in_direction(Direction::Right) && square.can_shift_in_direction(Direction::Up),
84 PushPullDirection::SE => square.can_shift_in_direction(Direction::Down) && square.can_shift_in_direction(Direction::Left),
85 PushPullDirection::SS => square.can_shift_in_direction(Direction::Down) && square.can_shift_in_direction(Direction::Up),
86 PushPullDirection::SW => square.can_shift_in_direction(Direction::Down) && square.can_shift_in_direction(Direction::Right),
87 PushPullDirection::WN => square.can_shift_in_direction(Direction::Left) && square.can_shift_in_direction(Direction::Down),
88 PushPullDirection::WS => square.can_shift_in_direction(Direction::Left) && square.can_shift_in_direction(Direction::Up),
89 PushPullDirection::WW => square.can_shift_in_direction(Direction::Left) && square.can_shift_in_direction(Direction::Right),
90 }
91 }
92}
93
94impl fmt::Display for PushPullDirection {
95 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96 match self {
97 PushPullDirection::NN => write!(f, "nn"),
98 PushPullDirection::NE => write!(f, "ne"),
99 PushPullDirection::NW => write!(f, "nw"),
100 PushPullDirection::EN => write!(f, "en"),
101 PushPullDirection::EE => write!(f, "ee"),
102 PushPullDirection::ES => write!(f, "es"),
103 PushPullDirection::SE => write!(f, "se"),
104 PushPullDirection::SS => write!(f, "ss"),
105 PushPullDirection::SW => write!(f, "sw"),
106 PushPullDirection::WN => write!(f, "wn"),
107 PushPullDirection::WS => write!(f, "ws"),
108 PushPullDirection::WW => write!(f, "ww"),
109 }
110 }
111}
112
113impl FromStr for PushPullDirection {
114 type Err = anyhow::Error;
115
116 fn from_str(s: &str) -> Result<Self, Self::Err> {
117 match s {
118 "nn" => Ok(PushPullDirection::NN),
119 "ne" => Ok(PushPullDirection::NE),
120 "nw" => Ok(PushPullDirection::NW),
121 "en" => Ok(PushPullDirection::EN),
122 "ee" => Ok(PushPullDirection::EE),
123 "es" => Ok(PushPullDirection::ES),
124 "se" => Ok(PushPullDirection::SE),
125 "ss" => Ok(PushPullDirection::SS),
126 "sw" => Ok(PushPullDirection::SW),
127 "wn" => Ok(PushPullDirection::WN),
128 "ws" => Ok(PushPullDirection::WS),
129 "ww" => Ok(PushPullDirection::WW),
130 _ => Err(anyhow!("Invalid value for direction")),
131 }
132 }
133}
134
135impl PushPullDirection {
136 pub fn invert(&self) -> Self {
137 match self {
138 PushPullDirection::NN => PushPullDirection::SS,
139 PushPullDirection::NE => PushPullDirection::SW,
140 PushPullDirection::NW => PushPullDirection::SE,
141 PushPullDirection::EN => PushPullDirection::WS,
142 PushPullDirection::EE => PushPullDirection::WW,
143 PushPullDirection::ES => PushPullDirection::WN,
144 PushPullDirection::SE => PushPullDirection::NW,
145 PushPullDirection::SS => PushPullDirection::NN,
146 PushPullDirection::SW => PushPullDirection::NE,
147 PushPullDirection::WN => PushPullDirection::ES,
148 PushPullDirection::WS => PushPullDirection::EN,
149 PushPullDirection::WW => PushPullDirection::EE,
150 }
151 }
152
153 pub fn vertical_symmetry(&self) -> Self {
154 match self {
155 PushPullDirection::NN => PushPullDirection::NN,
156 PushPullDirection::NE => PushPullDirection::NW,
157 PushPullDirection::NW => PushPullDirection::NE,
158 PushPullDirection::EN => PushPullDirection::WN,
159 PushPullDirection::EE => PushPullDirection::WW,
160 PushPullDirection::ES => PushPullDirection::WS,
161 PushPullDirection::SE => PushPullDirection::SW,
162 PushPullDirection::SS => PushPullDirection::SS,
163 PushPullDirection::SW => PushPullDirection::SE,
164 PushPullDirection::WN => PushPullDirection::EN,
165 PushPullDirection::WS => PushPullDirection::ES,
166 PushPullDirection::WW => PushPullDirection::EE,
167 }
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 fn assert_can_shift_in_direction(invalid_squares: &[&str], direction: PushPullDirection) {
176 for idx in 0..64 {
177 let square = Square::from_index(idx);
178 if invalid_squares.contains(&square.to_string().as_str()) {
179 assert!(!direction.is_valid_from_square(square));
180 } else {
181 assert!(direction.is_valid_from_square(square));
182 }
183 }
184 }
185
186 #[test]
187 fn test_is_valid_from_square_nn() {
188 let invalid_squares = ["a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"];
189 let direction = PushPullDirection::NN;
190
191 assert_can_shift_in_direction(&invalid_squares, direction);
192 }
193
194 #[test]
195 fn test_can_shift_in_direction_ne() {
196 let invalid_squares = ["a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", "a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1"];
197 let direction = PushPullDirection::NE;
198
199 assert_can_shift_in_direction(&invalid_squares, direction);
200 }
201
202 #[test]
203 fn test_can_shift_in_direction_nw() {
204 let invalid_squares = ["a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", "h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1"];
205 let direction = PushPullDirection::NW;
206
207 assert_can_shift_in_direction(&invalid_squares, direction);
208 }
209
210 #[test]
211 fn test_can_shift_in_direction_en() {
212 let invalid_squares = ["h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1", "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"];
213 let direction = PushPullDirection::EN;
214
215 assert_can_shift_in_direction(&invalid_squares, direction);
216 }
217
218 #[test]
219 fn test_can_shift_in_direction_ee() {
220 let invalid_squares = ["h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1", "a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1"];
221 let direction = PushPullDirection::EE;
222
223 assert_can_shift_in_direction(&invalid_squares, direction);
224 }
225
226 #[test]
227 fn test_can_shift_in_direction_es() {
228 let invalid_squares = ["h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1", "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"];
229 let direction = PushPullDirection::ES;
230
231 assert_can_shift_in_direction(&invalid_squares, direction);
232 }
233
234 #[test]
235 fn test_can_shift_in_direction_wn() {
236 let invalid_squares = ["a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1", "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"];
237 let direction = PushPullDirection::WN;
238
239 assert_can_shift_in_direction(&invalid_squares, direction);
240 }
241
242 #[test]
243 fn test_can_shift_in_direction_ws() {
244 let invalid_squares = ["a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1", "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"];
245 let direction = PushPullDirection::WS;
246
247 assert_can_shift_in_direction(&invalid_squares, direction);
248 }
249
250 #[test]
251 fn test_can_shift_in_direction_ww() {
252 let invalid_squares = ["a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1", "h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1"];
253 let direction = PushPullDirection::WW;
254
255 assert_can_shift_in_direction(&invalid_squares, direction);
256 }
257
258 #[test]
259 fn test_can_shift_in_direction_se() {
260 let invalid_squares = ["a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", "a8", "a7", "a6", "a5", "a4", "a3", "a2", "a1"];
261 let direction = PushPullDirection::SE;
262
263 assert_can_shift_in_direction(&invalid_squares, direction);
264 }
265
266 #[test]
267 fn test_can_shift_in_direction_ss() {
268 let invalid_squares = ["a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8"];
269 let direction = PushPullDirection::SS;
270
271 assert_can_shift_in_direction(&invalid_squares, direction);
272 }
273
274 #[test]
275 fn test_can_shift_in_direction_sw() {
276 let invalid_squares = ["a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", "h8", "h7", "h6", "h5", "h4", "h3", "h2", "h1"];
277 let direction = PushPullDirection::SW;
278
279 assert_can_shift_in_direction(&invalid_squares, direction);
280 }
281}