Skip to main content

arimaa_engine/
push_pull.rs

1use anyhow::anyhow;
2use std::fmt;
3use std::str::FromStr;
4
5use super::{Direction, Square};
6
7// Prescribed as the first being the direction of the piece which vacates the square and the second being the direction of piece to occupy the square.
8#[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}