Skip to main content

arimaa_engine/
action.rs

1use anyhow::anyhow;
2use serde::de::Error;
3use serde::de::{Deserialize, Deserializer, Error as DeserializeError, Unexpected, Visitor};
4use serde::ser::{Serialize, Serializer};
5use std::fmt;
6use std::str::FromStr;
7use tinyvec::ArrayVec;
8
9use super::bits::for_each_bit_idx;
10use super::{Direction, Path, PushPullDirection, Square};
11
12#[derive(Hash, Clone, Default, Eq, PartialEq)]
13pub enum Action {
14    Place(Square),
15    Move(Square, Path),
16    PushPull(Square, PushPullDirection),
17    #[default]
18    Pass,
19}
20
21impl Serialize for Action {
22    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
23    where
24        S: Serializer,
25    {
26        serializer.serialize_str(&format!("{}", self))
27    }
28}
29
30struct ActionVisitor {}
31
32impl ActionVisitor {
33    fn new() -> Self {
34        Self {}
35    }
36}
37
38impl<'de> Visitor<'de> for ActionVisitor {
39    type Value = Action;
40
41    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
42        formatter.write_str("Expecting a string with a letter representing the column then a number representing the row.")
43    }
44
45    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
46    where
47        E: Error,
48    {
49        v.parse::<Action>().map_err(|_| DeserializeError::invalid_value(Unexpected::Str(v), &self))
50    }
51}
52
53impl<'de> Deserialize<'de> for Action {
54    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
55    where
56        D: Deserializer<'de>,
57    {
58        deserializer.deserialize_str(ActionVisitor::new())
59    }
60}
61
62impl Action {
63    pub fn rotate(&self) -> Self {
64        match self {
65            Action::Move(square, path) => Action::Move(square.invert(), path.invert()),
66            Action::PushPull(square, direction) => Action::PushPull(square.invert(), direction.invert()),
67            Action::Place(square) => Action::Place(square.invert()),
68            Action::Pass => Action::Pass,
69        }
70    }
71
72    pub fn vertical_symmetry(&self) -> Self {
73        match self {
74            Action::Move(square, path) => Action::Move(square.vertical_symmetry(), path.vertical_symmetry()),
75            Action::PushPull(square, direction) => Action::PushPull(square.vertical_symmetry(), direction.vertical_symmetry()),
76            Action::Place(square) => Action::Place(square.vertical_symmetry()),
77            Action::Pass => Action::Pass,
78        }
79    }
80
81    pub fn get_steps(&self) -> ArrayVec<[(Square, Direction); 4]> {
82        match self {
83            Action::Move(square, path) => {
84                path.iter()
85                    .fold((*square, ArrayVec::new()), |(square, mut dirs), dir| {
86                        dirs.push((square, dir));
87                        (square.shift_in_direction(dir), dirs)
88                    })
89                    .1
90            }
91            Action::PushPull(square, direction) => direction.get_steps(*square).iter().cloned().collect(),
92            _ => panic!("Cannot get steps for action {}", self),
93        }
94    }
95
96    // Gets the square that a piece lands on.
97    pub fn landing_square(&self) -> Square {
98        match self {
99            Action::Move(square, path) => path.iter().fold(*square, |square, direction| square.shift_in_direction(direction)),
100            _ => panic!("Cannot get landing_square for action {}", self),
101        }
102    }
103}
104
105// Pass: p
106// Move: a5n, a5nees
107// PushPull: pa5ne
108// Place: e
109
110impl fmt::Display for Action {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        let action = match self {
113            Action::Move(square, path) => format!("{}{}", square, path),
114            Action::PushPull(square, direction) => format!("p{}{}", square, direction),
115            Action::Pass => "p".to_string(),
116            Action::Place(piece) => format!("{}", piece),
117        };
118
119        write!(f, "{}", action)
120    }
121}
122
123impl fmt::Debug for Action {
124    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
125        write!(f, "{}", self)
126    }
127}
128
129impl FromStr for Action {
130    type Err = anyhow::Error;
131
132    fn from_str(s: &str) -> Result<Self, Self::Err> {
133        let parse = || -> Option<Action> {
134            Some(match s.len() {
135                1 if s == "p" => Action::Pass,
136                2 => Action::Place(s.parse().ok()?),
137                5 if s.starts_with('p') => Action::PushPull(s[1..3].parse().ok()?, s[3..5].parse().ok()?),
138                3.. => Action::Move(s[..2].parse().ok()?, s[2..].parse().ok()?),
139                _ => return None,
140            })
141        };
142
143        parse().ok_or_else(|| anyhow!("Invalid action: {}", s))
144    }
145}
146
147pub fn map_bit_board_to_squares(board: u64) -> Vec<Square> {
148    let mut squares = Vec::with_capacity(board.count_ones() as usize);
149
150    for_each_bit_idx(board, |bit_idx| {
151        let square = Square::from_index(bit_idx as u8);
152        squares.push(square);
153    });
154
155    squares
156}
157
158#[cfg(test)]
159mod tests {
160    use super::super::Piece;
161
162    use super::*;
163    use serde_json::json;
164    use std::cmp::Ordering;
165
166    fn col_row_to_bit(col: usize, row: usize) -> u64 {
167        1 << ((col - 1) + (8 - row) * 8)
168    }
169
170    #[test]
171    fn test_as_bit_board_a1() {
172        let bit = Square::new('a', 1).as_bit_board();
173        let col = 1;
174        let row = 1;
175
176        assert_eq!(col_row_to_bit(col, row), bit);
177    }
178
179    #[test]
180    fn test_as_bit_board_a8() {
181        let bit = Square::new('a', 8).as_bit_board();
182        let col = 1;
183        let row = 8;
184
185        assert_eq!(col_row_to_bit(col, row), bit);
186    }
187
188    #[test]
189    fn test_as_bit_board_h1() {
190        let bit = Square::new('h', 1).as_bit_board();
191        let col = 8;
192        let row = 1;
193
194        assert_eq!(col_row_to_bit(col, row), bit);
195    }
196    #[test]
197    fn test_as_bit_board_h8() {
198        let bit = Square::new('h', 8).as_bit_board();
199        let col = 8;
200        let row = 8;
201
202        assert_eq!(col_row_to_bit(col, row), bit);
203    }
204
205    #[test]
206    fn test_as_bit_board_e5() {
207        let bit = Square::new('e', 5).as_bit_board();
208        let col = 5;
209        let row = 5;
210
211        assert_eq!(col_row_to_bit(col, row), bit);
212    }
213
214    #[test]
215    fn test_from_bit_board_a1() {
216        let col = 1;
217        let row = 1;
218        let bit_board = col_row_to_bit(col, row);
219
220        let square = Square::from_bit_board(bit_board);
221
222        assert_eq!(Square::new('a', 1), square);
223    }
224
225    #[test]
226    fn test_from_bit_board_a8() {
227        let col = 1;
228        let row = 8;
229        let bit_board = col_row_to_bit(col, row);
230
231        let square = Square::from_bit_board(bit_board);
232
233        assert_eq!(Square::new('a', 8), square);
234    }
235
236    #[test]
237    fn test_from_bit_board_h1() {
238        let col = 8;
239        let row = 1;
240        let bit_board = col_row_to_bit(col, row);
241
242        let square = Square::from_bit_board(bit_board);
243
244        assert_eq!(Square::new('h', 1), square);
245    }
246
247    #[test]
248    fn test_from_bit_board_h8() {
249        let col = 8;
250        let row = 8;
251        let bit_board = col_row_to_bit(col, row);
252
253        let square = Square::from_bit_board(bit_board);
254
255        assert_eq!(Square::new('h', 8), square);
256    }
257
258    #[test]
259    fn test_from_bit_board_e5() {
260        let col = 5;
261        let row = 5;
262        let bit_board = col_row_to_bit(col, row);
263
264        let square = Square::from_bit_board(bit_board);
265
266        assert_eq!(Square::new('e', 5), square);
267    }
268
269    #[test]
270    fn test_to_square_to_bit_board_from_bit_board_back_to_square() {
271        let cols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
272        let rows = [1, 2, 3, 4, 5, 6, 7, 8];
273
274        for (col, row) in cols.iter().zip(rows.iter()) {
275            let orig_square = Square::new(*col, *row);
276            let bit_board = orig_square.as_bit_board();
277            let square = Square::from_bit_board(bit_board);
278
279            assert_eq!(orig_square, square);
280        }
281    }
282
283    #[test]
284    fn test_invert_square_a1() {
285        let square = Square::new('a', 1);
286        let expected = Square::new('h', 8);
287
288        assert_eq!(square.invert(), expected);
289    }
290
291    #[test]
292    fn test_invert_square_h8() {
293        let square = Square::new('h', 8);
294        let expected = Square::new('a', 1);
295
296        assert_eq!(square.invert(), expected);
297    }
298
299    #[test]
300    fn test_invert_square_e5() {
301        let square = Square::new('e', 5);
302        let expected = Square::new('d', 4);
303
304        assert_eq!(square.invert(), expected);
305    }
306
307    #[test]
308    fn test_invert_square_d3() {
309        let square = Square::new('d', 3);
310        let expected = Square::new('e', 6);
311
312        assert_eq!(square.invert(), expected);
313    }
314
315    #[test]
316    fn test_invert_square_horizontal_a1() {
317        let square = Square::new('a', 1);
318        let expected = Square::new('h', 1);
319
320        assert_eq!(square.vertical_symmetry(), expected);
321    }
322
323    #[test]
324    fn test_invert_square_horizontal_h8() {
325        let square = Square::new('h', 8);
326        let expected = Square::new('a', 8);
327
328        assert_eq!(square.vertical_symmetry(), expected);
329    }
330
331    #[test]
332    fn test_invert_square_horizontal_e5() {
333        let square = Square::new('e', 5);
334        let expected = Square::new('d', 5);
335
336        assert_eq!(square.vertical_symmetry(), expected);
337    }
338
339    #[test]
340    fn test_invert_square_horizontal_d3() {
341        let square = Square::new('d', 3);
342        let expected = Square::new('e', 3);
343
344        assert_eq!(square.vertical_symmetry(), expected);
345    }
346
347    #[test]
348    fn test_invert_square_horizontal_double_invert() {
349        let square = Square::new('d', 3);
350
351        assert_eq!(square.vertical_symmetry().vertical_symmetry(), square);
352    }
353
354    #[test]
355    fn test_square_shift_in_direction_up() {
356        let square = Square::new('d', 3);
357        let shifted_square = square.shift_in_direction(Direction::Up);
358
359        assert_eq!(shifted_square, Square::new('d', 4));
360    }
361
362    #[test]
363    fn test_square_shift_in_direction_right() {
364        let square = Square::new('d', 3);
365        let shifted_square = square.shift_in_direction(Direction::Right);
366
367        assert_eq!(shifted_square, Square::new('e', 3));
368    }
369
370    #[test]
371    fn test_square_shift_in_direction_down() {
372        let square = Square::new('d', 3);
373        let shifted_square = square.shift_in_direction(Direction::Down);
374
375        assert_eq!(shifted_square, Square::new('d', 2));
376    }
377
378    #[test]
379    fn test_square_shift_in_direction_left() {
380        let square = Square::new('d', 3);
381        let shifted_square = square.shift_in_direction(Direction::Left);
382
383        assert_eq!(shifted_square, Square::new('c', 3));
384    }
385
386    #[test]
387    fn test_square_is_adjacent_same() {
388        let square = Square::new('d', 3);
389        let square_2 = Square::new('d', 3);
390
391        let is_adjacent = square.is_adjacent(&square_2);
392
393        assert!(!is_adjacent);
394    }
395
396    #[test]
397    fn test_square_is_adjacent_wrap() {
398        let square = Square::new('a', 1);
399        let square_2 = Square::new('h', 8);
400
401        let is_adjacent = square.is_adjacent(&square_2);
402
403        assert!(!is_adjacent);
404    }
405
406    #[test]
407    fn test_square_is_adjacent_column() {
408        let square = Square::new('b', 1);
409        let square_2 = Square::new('b', 2);
410
411        let is_adjacent = square.is_adjacent(&square_2);
412
413        assert!(!is_adjacent);
414    }
415
416    #[test]
417    fn test_square_is_adjacent_row() {
418        let square = Square::new('b', 2);
419        let square_2 = Square::new('a', 2);
420
421        let is_adjacent = square.is_adjacent(&square_2);
422
423        assert!(!is_adjacent);
424    }
425
426    #[test]
427    fn test_square_is_adjacent_column_row() {
428        let square = Square::new('a', 2);
429        let square_2 = Square::new('a', 2);
430
431        let is_adjacent = square.is_adjacent(&square_2);
432
433        assert!(!is_adjacent);
434    }
435
436    #[test]
437    fn test_square_is_adjacent_column_wrap_row() {
438        let square = Square::new('b', 1);
439        let square_2 = Square::new('b', 8);
440
441        let is_adjacent = square.is_adjacent(&square_2);
442
443        assert!(!is_adjacent);
444    }
445
446    #[test]
447    fn test_square_is_adjacent_column_wrap_column() {
448        let square = Square::new('a', 2);
449        let square_2 = Square::new('h', 2);
450
451        let is_adjacent = square.is_adjacent(&square_2);
452
453        assert!(!is_adjacent);
454    }
455
456    #[test]
457    fn test_piece_precedence_rabbit() {
458        assert_eq!(Piece::Rabbit.cmp(&Piece::Cat), Ordering::Less);
459        assert_eq!(Piece::Rabbit.cmp(&Piece::Rabbit), Ordering::Equal);
460        assert_eq!(Piece::Cat.cmp(&Piece::Rabbit), Ordering::Greater);
461    }
462
463    #[test]
464    fn test_piece_precedence_cat() {
465        assert_eq!(Piece::Cat.cmp(&Piece::Dog), Ordering::Less);
466        assert_eq!(Piece::Cat.cmp(&Piece::Cat), Ordering::Equal);
467        assert_eq!(Piece::Dog.cmp(&Piece::Cat), Ordering::Greater);
468    }
469
470    #[test]
471    fn test_piece_precedence_dog() {
472        assert_eq!(Piece::Dog.cmp(&Piece::Horse), Ordering::Less);
473        assert_eq!(Piece::Dog.cmp(&Piece::Dog), Ordering::Equal);
474        assert_eq!(Piece::Horse.cmp(&Piece::Dog), Ordering::Greater);
475    }
476
477    #[test]
478    fn test_piece_precedence_horse() {
479        assert_eq!(Piece::Horse.cmp(&Piece::Camel), Ordering::Less);
480        assert_eq!(Piece::Horse.cmp(&Piece::Horse), Ordering::Equal);
481        assert_eq!(Piece::Camel.cmp(&Piece::Horse), Ordering::Greater);
482    }
483
484    #[test]
485    fn test_piece_precedence_camel() {
486        assert_eq!(Piece::Camel.cmp(&Piece::Elephant), Ordering::Less);
487        assert_eq!(Piece::Camel.cmp(&Piece::Camel), Ordering::Equal);
488        assert_eq!(Piece::Elephant.cmp(&Piece::Camel), Ordering::Greater);
489    }
490
491    #[test]
492    fn test_piece_precedence_elephant() {
493        assert_eq!(Piece::Elephant.cmp(&Piece::Elephant), Ordering::Equal);
494        assert_eq!(Piece::Elephant.cmp(&Piece::Rabbit), Ordering::Greater);
495    }
496
497    #[test]
498    fn test_action_move_ser_json() {
499        let action = Action::Move(Square::new('a', 1), "n".parse().unwrap());
500        let serialized_action_as_json = json!(action);
501
502        assert_eq!(serialized_action_as_json, "a1n");
503    }
504
505    #[test]
506    fn test_action_move_ser_json_2() {
507        let action = Action::Move(Square::new('d', 4), "e".parse().unwrap());
508        let serialized_action_as_json = json!(action);
509
510        assert_eq!(serialized_action_as_json, "d4e");
511    }
512
513    #[test]
514    fn test_action_place_a1_ser_json() {
515        let action = Action::Place(Square::new('a', 1));
516        let serialized_action_as_json = json!(action);
517
518        assert_eq!(serialized_action_as_json, "a1");
519    }
520
521    #[test]
522    fn test_action_place_h8_ser_json() {
523        let action = Action::Place(Square::new('h', 8));
524        let serialized_action_as_json = json!(action);
525
526        assert_eq!(serialized_action_as_json, "h8");
527    }
528
529    #[test]
530    fn test_action_pass_ser_json() {
531        let action = Action::Pass;
532        let serialized_action_as_json = json!(action);
533
534        assert_eq!(serialized_action_as_json, "p");
535    }
536
537    #[test]
538    fn test_action_deser_move() {
539        let json = "\"b2w\"";
540
541        assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Move(Square::new('b', 2), "w".parse().unwrap()),)
542    }
543
544    #[test]
545    fn test_action_deser_place() {
546        let json = "\"a1\"";
547
548        assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Place(Square::new('a', 1)),);
549    }
550
551    #[test]
552    fn test_action_deser_pass() {
553        let json = "\"p\"";
554
555        assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Pass,);
556    }
557
558    #[test]
559    fn test_action_landing_square() {
560        let action: Action = "d5n".parse().unwrap();
561        let landing_square = action.landing_square();
562
563        assert_eq!(landing_square, "d6".parse().unwrap());
564    }
565
566    #[test]
567    fn test_action_landing_square_2() {
568        let action: Action = "d5nn".parse().unwrap();
569        let landing_square = action.landing_square();
570
571        assert_eq!(landing_square, "d7".parse().unwrap());
572    }
573
574    #[test]
575    fn test_action_landing_square_3() {
576        let action: Action = "d5nne".parse().unwrap();
577        let landing_square = action.landing_square();
578
579        assert_eq!(landing_square, "e7".parse().unwrap());
580    }
581
582    #[test]
583    fn test_path_iter() {
584        for paths in super::super::path_permutation::get_path_permutations(4) {
585            for (path, pre_generated_path) in paths {
586                let path_from = Path::from(path.as_slice());
587                let path_from_iter = path.iter().collect::<Path>();
588
589                assert_eq!(path_from, path_from_iter);
590                assert_eq!(path_from, *pre_generated_path);
591
592                let path_into_vec = path_from.iter().collect::<ArrayVec<[Direction; 4]>>();
593
594                assert_eq!(path_into_vec, *path);
595            }
596        }
597    }
598}