use anyhow::anyhow;
use std::convert::TryFrom;
use std::fmt;
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
#[derive(Default)]
pub enum Direction {
#[default]
Up,
Right,
Down,
Left,
}
#[derive(Clone, Copy, Debug, EnumIter, Eq, Hash, PartialEq)]
#[derive(Default)]
pub enum MoveDirection {
#[default]
N,
E,
S,
W,
NN,
NE,
NW,
EE,
ES,
SS,
SW,
WW,
NNN,
NNE,
NNW,
NEE,
NWW,
EEE,
EES,
ESS,
SSS,
SSW,
SWW,
WWW,
NNNN,
NNNE,
NNNW,
NNEE,
NNWW,
NEEE,
NWWW,
EEEE,
EEES,
EESS,
ESSS,
SSSS,
SSSW,
SSWW,
SWWW,
WWWW,
}
impl MoveDirection {
#[rustfmt::skip]
pub fn directions(&self) -> Vec<Direction> {
match self {
MoveDirection::N => vec![Direction::Up ],
MoveDirection::E => vec![Direction::Right],
MoveDirection::S => vec![Direction::Down ],
MoveDirection::W => vec![Direction::Left ],
MoveDirection::NN => vec![Direction::Up, Direction::Up ],
MoveDirection::NE => vec![Direction::Up, Direction::Right],
MoveDirection::NW => vec![Direction::Up, Direction::Left ],
MoveDirection::EE => vec![Direction::Right, Direction::Right],
MoveDirection::ES => vec![Direction::Right, Direction::Down ],
MoveDirection::SS => vec![Direction::Down, Direction::Down ],
MoveDirection::SW => vec![Direction::Down, Direction::Left ],
MoveDirection::WW => vec![Direction::Left, Direction::Left ],
MoveDirection::NNN => vec![Direction::Up, Direction::Up, Direction::Up ],
MoveDirection::NNE => vec![Direction::Up, Direction::Up, Direction::Right],
MoveDirection::NNW => vec![Direction::Up, Direction::Up, Direction::Left ],
MoveDirection::NEE => vec![Direction::Up, Direction::Right, Direction::Right],
MoveDirection::NWW => vec![Direction::Up, Direction::Left, Direction::Left ],
MoveDirection::EEE => vec![Direction::Right, Direction::Right, Direction::Right],
MoveDirection::EES => vec![Direction::Right, Direction::Right, Direction::Down ],
MoveDirection::ESS => vec![Direction::Right, Direction::Down, Direction::Down ],
MoveDirection::SSS => vec![Direction::Down, Direction::Down, Direction::Down ],
MoveDirection::SSW => vec![Direction::Down, Direction::Down, Direction::Left ],
MoveDirection::SWW => vec![Direction::Down, Direction::Left, Direction::Left ],
MoveDirection::WWW => vec![Direction::Left, Direction::Left, Direction::Left ],
MoveDirection::NNNN => vec![Direction::Up, Direction::Up, Direction::Up, Direction::Up ],
MoveDirection::NNNE => vec![Direction::Up, Direction::Up, Direction::Up, Direction::Right],
MoveDirection::NNNW => vec![Direction::Up, Direction::Up, Direction::Up, Direction::Left ],
MoveDirection::NNEE => vec![Direction::Up, Direction::Up, Direction::Right, Direction::Right],
MoveDirection::NNWW => vec![Direction::Up, Direction::Up, Direction::Left, Direction::Left ],
MoveDirection::NEEE => vec![Direction::Up, Direction::Right, Direction::Right, Direction::Right],
MoveDirection::NWWW => vec![Direction::Up, Direction::Left, Direction::Left, Direction::Left ],
MoveDirection::EEEE => vec![Direction::Right, Direction::Right, Direction::Right, Direction::Right],
MoveDirection::EEES => vec![Direction::Right, Direction::Right, Direction::Right, Direction::Down ],
MoveDirection::EESS => vec![Direction::Right, Direction::Right, Direction::Down, Direction::Down ],
MoveDirection::ESSS => vec![Direction::Right, Direction::Down, Direction::Down, Direction::Down ],
MoveDirection::SSSS => vec![Direction::Down, Direction::Down, Direction::Down, Direction::Down ],
MoveDirection::SSSW => vec![Direction::Down, Direction::Down, Direction::Down, Direction::Left ],
MoveDirection::SSWW => vec![Direction::Down, Direction::Down, Direction::Left, Direction::Left ],
MoveDirection::SWWW => vec![Direction::Down, Direction::Left, Direction::Left, Direction::Left ],
MoveDirection::WWWW => vec![Direction::Left, Direction::Left, Direction::Left, Direction::Left ],
}
}
pub fn move_directions() -> Vec<MoveDirection> {
vec![
MoveDirection::N,
MoveDirection::E,
MoveDirection::S,
MoveDirection::W,
MoveDirection::NN,
MoveDirection::NE,
MoveDirection::NW,
MoveDirection::EE,
MoveDirection::ES,
MoveDirection::SS,
MoveDirection::SW,
MoveDirection::WW,
MoveDirection::NNN,
MoveDirection::NNE,
MoveDirection::NNW,
MoveDirection::NEE,
MoveDirection::NWW,
MoveDirection::EEE,
MoveDirection::EES,
MoveDirection::ESS,
MoveDirection::SSS,
MoveDirection::SSW,
MoveDirection::SWW,
MoveDirection::WWW,
MoveDirection::NNNN,
MoveDirection::NNNE,
MoveDirection::NNNW,
MoveDirection::NNEE,
MoveDirection::NNWW,
MoveDirection::NEEE,
MoveDirection::NWWW,
MoveDirection::EEEE,
MoveDirection::EEES,
MoveDirection::EESS,
MoveDirection::ESSS,
MoveDirection::SSSS,
MoveDirection::SSSW,
MoveDirection::SSWW,
MoveDirection::SWWW,
MoveDirection::WWWW,
]
}
}
impl TryFrom<&[Direction]> for MoveDirection {
type Error = anyhow::Error;
fn try_from(dirs: &[Direction]) -> Result<Self, Self::Error> {
let vector = |dirs: &[Direction]| -> (i8, i8) {
dirs.iter().fold((0, 0), |(mut x, mut y), dir| {
match dir {
Direction::Up => y += 1,
Direction::Right => x += 1,
Direction::Down => y -= 1,
Direction::Left => x -= 1,
};
(x, y)
})
};
let dir_vector = vector(dirs);
MoveDirection::iter()
.find(|move_direction| vector(&move_direction.directions()) == dir_vector)
.ok_or_else(|| anyhow!("No matching MoveDirection for {:?}", dirs))
}
}
impl Direction {
pub fn invert(&self) -> Self {
match self {
Direction::Up => Direction::Down,
Direction::Right => Direction::Left,
Direction::Down => Direction::Up,
Direction::Left => Direction::Right,
}
}
pub fn vertical_symmetry(&self) -> Self {
match self {
Direction::Up => Direction::Up,
Direction::Right => Direction::Left,
Direction::Down => Direction::Down,
Direction::Left => Direction::Right,
}
}
}
impl TryFrom<char> for Direction {
type Error = anyhow::Error;
fn try_from(c: char) -> Result<Direction, anyhow::Error> {
match c {
'n' => Ok(Direction::Up),
'e' => Ok(Direction::Right),
's' => Ok(Direction::Down),
'w' => Ok(Direction::Left),
_ => Err(anyhow!("Invalid character for Direction")),
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let dir = match self {
Direction::Up => 'n',
Direction::Right => 'e',
Direction::Down => 's',
Direction::Left => 'w',
};
write!(f, "{}", dir)
}
}
impl FromStr for Direction {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut chars = s.chars();
match chars.next() {
Some(c) => match chars.next() {
None => Direction::try_from(c),
Some(_) => Err(anyhow!("More than one char found for Direction: {}", s)),
},
None => Err(anyhow!("No char found for Direction: {}", s)),
}
}
}