use std::cmp::Ordering;
use thiserror::Error;
use crate::{
algorithm::{
direction::Direction,
r#move::{r#move::Move, try_into_move::TryIntoMove},
},
puzzle::sliding_puzzle::SlidingPuzzle,
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PositionMove(pub u64, pub u64);
#[derive(Clone, Debug, Error, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TryPositionMoveIntoMoveError {
#[error("InvalidMove: position ({0}, {1}) can not be moved")]
InvalidMove(u64, u64),
}
impl<Puzzle: SlidingPuzzle> TryIntoMove<Puzzle> for PositionMove {
type Error = TryPositionMoveIntoMoveError;
fn try_into_move(&self, puzzle: &Puzzle) -> Result<Move, Self::Error> {
let Self(x, y) = *self;
if puzzle.can_move_position_xy((x, y)) {
let (gx, gy) = puzzle.gap_position_xy();
if x == gx {
match y.cmp(&gy) {
Ordering::Less => Ok(Move::new(Direction::Down, gy - y)),
Ordering::Greater => Ok(Move::new(Direction::Up, y - gy)),
Ordering::Equal => Ok(Move::new(Direction::Up, 0)),
}
} else {
match x.cmp(&gx) {
Ordering::Less => Ok(Move::new(Direction::Right, gx - x)),
Ordering::Greater => Ok(Move::new(Direction::Left, x - gx)),
Ordering::Equal => Ok(Move::new(Direction::Right, 0)),
}
}
} else {
Err(Self::Error::InvalidMove(x, y))
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr as _;
use crate::{
algorithm::r#move::{
position_move::{PositionMove, TryPositionMoveIntoMoveError},
r#move::Move,
try_into_move::TryIntoMove as _,
},
puzzle::puzzle::Puzzle,
};
#[test]
fn test_try_into_move() {
let p = Puzzle::from_str("7 4 14 10/11 0 12 5/1 9 2 3/15 8 6 13").unwrap();
assert_eq!(
PositionMove(3, 1).try_into_move(&p),
Ok(Move::from_str("L2").unwrap())
);
assert_eq!(
PositionMove(1, 3).try_into_move(&p),
Ok(Move::from_str("U2").unwrap())
);
assert_eq!(
PositionMove(0, 1).try_into_move(&p),
Ok(Move::from_str("R").unwrap())
);
assert_eq!(
PositionMove(1, 0).try_into_move(&p),
Ok(Move::from_str("D").unwrap())
);
assert_eq!(
PositionMove(0, 0).try_into_move(&p),
Err(TryPositionMoveIntoMoveError::InvalidMove(0, 0))
);
}
}