use rand::distr::{Distribution, StandardUniform};
use std::{
fmt::{Display, Write as _},
str::FromStr,
};
use thiserror::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Direction {
Up,
Left,
Down,
Right,
}
impl Direction {
#[must_use]
pub fn inverse(&self) -> Self {
match self {
Self::Up => Self::Down,
Self::Left => Self::Right,
Self::Down => Self::Up,
Self::Right => Self::Left,
}
}
#[must_use]
pub fn transpose(&self) -> Self {
match self {
Self::Up => Self::Left,
Self::Left => Self::Up,
Self::Down => Self::Right,
Self::Right => Self::Down,
}
}
#[must_use]
pub fn reflect_left_right(&self) -> Self {
match self {
Self::Up => Self::Up,
Self::Left => Self::Right,
Self::Down => Self::Down,
Self::Right => Self::Left,
}
}
#[must_use]
pub fn reflect_up_down(&self) -> Self {
match self {
Self::Up => Self::Down,
Self::Left => Self::Left,
Self::Down => Self::Up,
Self::Right => Self::Right,
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_char(match self {
Self::Up => 'U',
Self::Left => 'L',
Self::Down => 'D',
Self::Right => 'R',
})
}
}
#[derive(Clone, Debug, Error, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum ParseDirectionError {
#[error("InvalidCharacter: character {0} must be one of 'U', 'L', 'D', 'R'")]
InvalidCharacter(char),
#[error("Empty: string is empty")]
Empty,
}
impl TryFrom<char> for Direction {
type Error = ParseDirectionError;
fn try_from(value: char) -> Result<Self, Self::Error> {
match value {
'U' => Ok(Self::Up),
'L' => Ok(Self::Left),
'D' => Ok(Self::Down),
'R' => Ok(Self::Right),
_ => Err(Self::Error::InvalidCharacter(value)),
}
}
}
impl FromStr for Direction {
type Err = ParseDirectionError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"U" => Ok(Self::Up),
"L" => Ok(Self::Left),
"D" => Ok(Self::Down),
"R" => Ok(Self::Right),
_ => Err(s
.chars()
.next()
.map_or(Self::Err::Empty, Self::Err::InvalidCharacter)),
}
}
}
impl Distribution<Direction> for StandardUniform {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Direction {
match rng.random_range(0..4) {
0 => Direction::Up,
1 => Direction::Left,
2 => Direction::Down,
3 => Direction::Right,
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr as _;
use crate::algorithm::direction::{Direction, ParseDirectionError};
#[test]
fn test_from_str() {
assert_eq!(Direction::from_str("U"), Ok(Direction::Up));
assert_eq!(
Direction::from_str("🥚"),
Err(ParseDirectionError::InvalidCharacter('🥚'))
);
assert_eq!(Direction::from_str(""), Err(ParseDirectionError::Empty));
}
}