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 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
105impl 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}