use anyhow::anyhow;
use serde::de::Error;
use serde::de::{Deserialize, Deserializer, Error as DeserializeError, Unexpected, Visitor};
use serde::ser::{Serialize, Serializer};
use std::fmt;
use std::str::FromStr;
use tinyvec::ArrayVec;
use super::bits::for_each_bit_idx;
use super::{Direction, Path, PushPullDirection, Square};
#[derive(Hash, Clone, Default, Eq, PartialEq)]
pub enum Action {
Place(Square),
Move(Square, Path),
PushPull(Square, PushPullDirection),
#[default]
Pass,
}
impl Serialize for Action {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&format!("{}", self))
}
}
struct ActionVisitor {}
impl ActionVisitor {
fn new() -> Self {
Self {}
}
}
impl<'de> Visitor<'de> for ActionVisitor {
type Value = Action;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("Expecting a string with a letter representing the column then a number representing the row.")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
v.parse::<Action>().map_err(|_| DeserializeError::invalid_value(Unexpected::Str(v), &self))
}
}
impl<'de> Deserialize<'de> for Action {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(ActionVisitor::new())
}
}
impl Action {
pub fn rotate(&self) -> Self {
match self {
Action::Move(square, path) => Action::Move(square.invert(), path.invert()),
Action::PushPull(square, direction) => Action::PushPull(square.invert(), direction.invert()),
Action::Place(square) => Action::Place(square.invert()),
Action::Pass => Action::Pass,
}
}
pub fn vertical_symmetry(&self) -> Self {
match self {
Action::Move(square, path) => Action::Move(square.vertical_symmetry(), path.vertical_symmetry()),
Action::PushPull(square, direction) => Action::PushPull(square.vertical_symmetry(), direction.vertical_symmetry()),
Action::Place(square) => Action::Place(square.vertical_symmetry()),
Action::Pass => Action::Pass,
}
}
pub fn get_steps(&self) -> ArrayVec<[(Square, Direction); 4]> {
match self {
Action::Move(square, path) => {
path.iter()
.fold((*square, ArrayVec::new()), |(square, mut dirs), dir| {
dirs.push((square, dir));
(square.shift_in_direction(dir), dirs)
})
.1
}
Action::PushPull(square, direction) => direction.get_steps(*square).iter().cloned().collect(),
_ => panic!("Cannot get steps for action {}", self),
}
}
pub fn landing_square(&self) -> Square {
match self {
Action::Move(square, path) => path.iter().fold(*square, |square, direction| square.shift_in_direction(direction)),
_ => panic!("Cannot get landing_square for action {}", self),
}
}
}
impl fmt::Display for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let action = match self {
Action::Move(square, path) => format!("{}{}", square, path),
Action::PushPull(square, direction) => format!("p{}{}", square, direction),
Action::Pass => "p".to_string(),
Action::Place(piece) => format!("{}", piece),
};
write!(f, "{}", action)
}
}
impl fmt::Debug for Action {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl FromStr for Action {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parse = || -> Option<Action> {
Some(match s.len() {
1 if s == "p" => Action::Pass,
2 => Action::Place(s.parse().ok()?),
5 if s.starts_with('p') => Action::PushPull(s[1..3].parse().ok()?, s[3..5].parse().ok()?),
3.. => Action::Move(s[..2].parse().ok()?, s[2..].parse().ok()?),
_ => return None,
})
};
parse().ok_or_else(|| anyhow!("Invalid action: {}", s))
}
}
pub fn map_bit_board_to_squares(board: u64) -> Vec<Square> {
let mut squares = Vec::with_capacity(board.count_ones() as usize);
for_each_bit_idx(board, |bit_idx| {
let square = Square::from_index(bit_idx as u8);
squares.push(square);
});
squares
}
#[cfg(test)]
mod tests {
use super::super::Piece;
use super::*;
use serde_json::json;
use std::cmp::Ordering;
fn col_row_to_bit(col: usize, row: usize) -> u64 {
1 << ((col - 1) + (8 - row) * 8)
}
#[test]
fn test_as_bit_board_a1() {
let bit = Square::new('a', 1).as_bit_board();
let col = 1;
let row = 1;
assert_eq!(col_row_to_bit(col, row), bit);
}
#[test]
fn test_as_bit_board_a8() {
let bit = Square::new('a', 8).as_bit_board();
let col = 1;
let row = 8;
assert_eq!(col_row_to_bit(col, row), bit);
}
#[test]
fn test_as_bit_board_h1() {
let bit = Square::new('h', 1).as_bit_board();
let col = 8;
let row = 1;
assert_eq!(col_row_to_bit(col, row), bit);
}
#[test]
fn test_as_bit_board_h8() {
let bit = Square::new('h', 8).as_bit_board();
let col = 8;
let row = 8;
assert_eq!(col_row_to_bit(col, row), bit);
}
#[test]
fn test_as_bit_board_e5() {
let bit = Square::new('e', 5).as_bit_board();
let col = 5;
let row = 5;
assert_eq!(col_row_to_bit(col, row), bit);
}
#[test]
fn test_from_bit_board_a1() {
let col = 1;
let row = 1;
let bit_board = col_row_to_bit(col, row);
let square = Square::from_bit_board(bit_board);
assert_eq!(Square::new('a', 1), square);
}
#[test]
fn test_from_bit_board_a8() {
let col = 1;
let row = 8;
let bit_board = col_row_to_bit(col, row);
let square = Square::from_bit_board(bit_board);
assert_eq!(Square::new('a', 8), square);
}
#[test]
fn test_from_bit_board_h1() {
let col = 8;
let row = 1;
let bit_board = col_row_to_bit(col, row);
let square = Square::from_bit_board(bit_board);
assert_eq!(Square::new('h', 1), square);
}
#[test]
fn test_from_bit_board_h8() {
let col = 8;
let row = 8;
let bit_board = col_row_to_bit(col, row);
let square = Square::from_bit_board(bit_board);
assert_eq!(Square::new('h', 8), square);
}
#[test]
fn test_from_bit_board_e5() {
let col = 5;
let row = 5;
let bit_board = col_row_to_bit(col, row);
let square = Square::from_bit_board(bit_board);
assert_eq!(Square::new('e', 5), square);
}
#[test]
fn test_to_square_to_bit_board_from_bit_board_back_to_square() {
let cols = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'];
let rows = [1, 2, 3, 4, 5, 6, 7, 8];
for (col, row) in cols.iter().zip(rows.iter()) {
let orig_square = Square::new(*col, *row);
let bit_board = orig_square.as_bit_board();
let square = Square::from_bit_board(bit_board);
assert_eq!(orig_square, square);
}
}
#[test]
fn test_invert_square_a1() {
let square = Square::new('a', 1);
let expected = Square::new('h', 8);
assert_eq!(square.invert(), expected);
}
#[test]
fn test_invert_square_h8() {
let square = Square::new('h', 8);
let expected = Square::new('a', 1);
assert_eq!(square.invert(), expected);
}
#[test]
fn test_invert_square_e5() {
let square = Square::new('e', 5);
let expected = Square::new('d', 4);
assert_eq!(square.invert(), expected);
}
#[test]
fn test_invert_square_d3() {
let square = Square::new('d', 3);
let expected = Square::new('e', 6);
assert_eq!(square.invert(), expected);
}
#[test]
fn test_invert_square_horizontal_a1() {
let square = Square::new('a', 1);
let expected = Square::new('h', 1);
assert_eq!(square.vertical_symmetry(), expected);
}
#[test]
fn test_invert_square_horizontal_h8() {
let square = Square::new('h', 8);
let expected = Square::new('a', 8);
assert_eq!(square.vertical_symmetry(), expected);
}
#[test]
fn test_invert_square_horizontal_e5() {
let square = Square::new('e', 5);
let expected = Square::new('d', 5);
assert_eq!(square.vertical_symmetry(), expected);
}
#[test]
fn test_invert_square_horizontal_d3() {
let square = Square::new('d', 3);
let expected = Square::new('e', 3);
assert_eq!(square.vertical_symmetry(), expected);
}
#[test]
fn test_invert_square_horizontal_double_invert() {
let square = Square::new('d', 3);
assert_eq!(square.vertical_symmetry().vertical_symmetry(), square);
}
#[test]
fn test_square_shift_in_direction_up() {
let square = Square::new('d', 3);
let shifted_square = square.shift_in_direction(Direction::Up);
assert_eq!(shifted_square, Square::new('d', 4));
}
#[test]
fn test_square_shift_in_direction_right() {
let square = Square::new('d', 3);
let shifted_square = square.shift_in_direction(Direction::Right);
assert_eq!(shifted_square, Square::new('e', 3));
}
#[test]
fn test_square_shift_in_direction_down() {
let square = Square::new('d', 3);
let shifted_square = square.shift_in_direction(Direction::Down);
assert_eq!(shifted_square, Square::new('d', 2));
}
#[test]
fn test_square_shift_in_direction_left() {
let square = Square::new('d', 3);
let shifted_square = square.shift_in_direction(Direction::Left);
assert_eq!(shifted_square, Square::new('c', 3));
}
#[test]
fn test_square_is_adjacent_same() {
let square = Square::new('d', 3);
let square_2 = Square::new('d', 3);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_wrap() {
let square = Square::new('a', 1);
let square_2 = Square::new('h', 8);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_column() {
let square = Square::new('b', 1);
let square_2 = Square::new('b', 2);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_row() {
let square = Square::new('b', 2);
let square_2 = Square::new('a', 2);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_column_row() {
let square = Square::new('a', 2);
let square_2 = Square::new('a', 2);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_column_wrap_row() {
let square = Square::new('b', 1);
let square_2 = Square::new('b', 8);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_square_is_adjacent_column_wrap_column() {
let square = Square::new('a', 2);
let square_2 = Square::new('h', 2);
let is_adjacent = square.is_adjacent(&square_2);
assert!(!is_adjacent);
}
#[test]
fn test_piece_precedence_rabbit() {
assert_eq!(Piece::Rabbit.cmp(&Piece::Cat), Ordering::Less);
assert_eq!(Piece::Rabbit.cmp(&Piece::Rabbit), Ordering::Equal);
assert_eq!(Piece::Cat.cmp(&Piece::Rabbit), Ordering::Greater);
}
#[test]
fn test_piece_precedence_cat() {
assert_eq!(Piece::Cat.cmp(&Piece::Dog), Ordering::Less);
assert_eq!(Piece::Cat.cmp(&Piece::Cat), Ordering::Equal);
assert_eq!(Piece::Dog.cmp(&Piece::Cat), Ordering::Greater);
}
#[test]
fn test_piece_precedence_dog() {
assert_eq!(Piece::Dog.cmp(&Piece::Horse), Ordering::Less);
assert_eq!(Piece::Dog.cmp(&Piece::Dog), Ordering::Equal);
assert_eq!(Piece::Horse.cmp(&Piece::Dog), Ordering::Greater);
}
#[test]
fn test_piece_precedence_horse() {
assert_eq!(Piece::Horse.cmp(&Piece::Camel), Ordering::Less);
assert_eq!(Piece::Horse.cmp(&Piece::Horse), Ordering::Equal);
assert_eq!(Piece::Camel.cmp(&Piece::Horse), Ordering::Greater);
}
#[test]
fn test_piece_precedence_camel() {
assert_eq!(Piece::Camel.cmp(&Piece::Elephant), Ordering::Less);
assert_eq!(Piece::Camel.cmp(&Piece::Camel), Ordering::Equal);
assert_eq!(Piece::Elephant.cmp(&Piece::Camel), Ordering::Greater);
}
#[test]
fn test_piece_precedence_elephant() {
assert_eq!(Piece::Elephant.cmp(&Piece::Elephant), Ordering::Equal);
assert_eq!(Piece::Elephant.cmp(&Piece::Rabbit), Ordering::Greater);
}
#[test]
fn test_action_move_ser_json() {
let action = Action::Move(Square::new('a', 1), "n".parse().unwrap());
let serialized_action_as_json = json!(action);
assert_eq!(serialized_action_as_json, "a1n");
}
#[test]
fn test_action_move_ser_json_2() {
let action = Action::Move(Square::new('d', 4), "e".parse().unwrap());
let serialized_action_as_json = json!(action);
assert_eq!(serialized_action_as_json, "d4e");
}
#[test]
fn test_action_place_a1_ser_json() {
let action = Action::Place(Square::new('a', 1));
let serialized_action_as_json = json!(action);
assert_eq!(serialized_action_as_json, "a1");
}
#[test]
fn test_action_place_h8_ser_json() {
let action = Action::Place(Square::new('h', 8));
let serialized_action_as_json = json!(action);
assert_eq!(serialized_action_as_json, "h8");
}
#[test]
fn test_action_pass_ser_json() {
let action = Action::Pass;
let serialized_action_as_json = json!(action);
assert_eq!(serialized_action_as_json, "p");
}
#[test]
fn test_action_deser_move() {
let json = "\"b2w\"";
assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Move(Square::new('b', 2), "w".parse().unwrap()),)
}
#[test]
fn test_action_deser_place() {
let json = "\"a1\"";
assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Place(Square::new('a', 1)),);
}
#[test]
fn test_action_deser_pass() {
let json = "\"p\"";
assert_eq!(serde_json::from_str::<Action>(json).unwrap(), Action::Pass,);
}
#[test]
fn test_action_landing_square() {
let action: Action = "d5n".parse().unwrap();
let landing_square = action.landing_square();
assert_eq!(landing_square, "d6".parse().unwrap());
}
#[test]
fn test_action_landing_square_2() {
let action: Action = "d5nn".parse().unwrap();
let landing_square = action.landing_square();
assert_eq!(landing_square, "d7".parse().unwrap());
}
#[test]
fn test_action_landing_square_3() {
let action: Action = "d5nne".parse().unwrap();
let landing_square = action.landing_square();
assert_eq!(landing_square, "e7".parse().unwrap());
}
#[test]
fn test_path_iter() {
for paths in super::super::path_permutation::get_path_permutations(4) {
for (path, pre_generated_path) in paths {
let path_from = Path::from(path.as_slice());
let path_from_iter = path.iter().collect::<Path>();
assert_eq!(path_from, path_from_iter);
assert_eq!(path_from, *pre_generated_path);
let path_into_vec = path_from.iter().collect::<ArrayVec<[Direction; 4]>>();
assert_eq!(path_into_vec, *path);
}
}
}
}