use std::fmt::{Debug, Display};
use std::iter::{IntoIterator, Iterator};
use itertools::{self, Itertools};
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Direction {
Up = 0,
NorthEast = 1,
Right = 2,
SouthEast = 3,
Down = 4,
SouthWest = 5,
Left = 6,
NorthWest = 7,
}
impl Direction {
pub const COUNT: usize = 8;
pub const ALL: [Direction; Direction::COUNT] = [
Direction::Up,
Direction::NorthEast,
Direction::Right,
Direction::SouthEast,
Direction::Down,
Direction::SouthWest,
Direction::Left,
Direction::NorthWest,
];
#[expect(clippy::as_conversions)]
pub fn opposite(&self) -> Direction { OPPOSITES[usize::from(*self as u8)] }
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Direction::{self:?}")
}
}
const OPPOSITES: [Direction; Direction::COUNT] = [
Direction::Down,
Direction::SouthWest,
Direction::Left,
Direction::NorthWest,
Direction::Up,
Direction::NorthEast,
Direction::Right,
Direction::SouthEast,
];
const EMPTY: u8 = 0;
const DIRECTIONS_FLAGS: [u8; Direction::COUNT] = [1, 2, 4, 8, 16, 32, 64, 128];
const FLAGS_DIRECTIONS: [Direction; Direction::COUNT] = [
Direction::Up,
Direction::NorthEast,
Direction::Right,
Direction::SouthEast,
Direction::Down,
Direction::SouthWest,
Direction::Left,
Direction::NorthWest,
];
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DirectionsSet(u8);
impl DirectionsSet {
pub fn is_empty(&self) -> bool { self.0 == EMPTY }
#[expect(clippy::as_conversions)]
pub fn contains(&self, direction: Direction) -> bool {
self.0 & DIRECTIONS_FLAGS[usize::from(direction as u8)] != 0
}
pub fn iter(&self) -> DirectionsSetIter { DirectionsSetIter::new(*self) }
}
impl Display for DirectionsSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{{}}}", self.iter().map(|d| format!("{d}")).join(", "))
}
}
impl Default for DirectionsSet {
fn default() -> Self { Self(EMPTY) }
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DirectionsSetIter {
src: u8,
current: usize,
}
impl DirectionsSetIter {
fn new(src: DirectionsSet) -> DirectionsSetIter {
DirectionsSetIter {
src: src.0,
current: 0,
}
}
}
impl Iterator for DirectionsSetIter {
type Item = Direction;
fn next(&mut self) -> Option<Direction> {
while self.current < Direction::COUNT {
let idx = self.current;
self.current += 1;
if self.src & DIRECTIONS_FLAGS[idx] != 0 {
return Some(FLAGS_DIRECTIONS[idx]);
}
}
None
}
}
impl IntoIterator for &DirectionsSet {
type Item = Direction;
type IntoIter = DirectionsSetIter;
fn into_iter(self) -> Self::IntoIter { DirectionsSetIter::new(*self) }
}
impl IntoIterator for DirectionsSet {
type Item = Direction;
type IntoIter = DirectionsSetIter;
fn into_iter(self) -> Self::IntoIter { DirectionsSetIter::new(self) }
}
impl<const N: usize> From<&[Direction; N]> for DirectionsSet {
fn from(data: &[Direction; N]) -> Self { Self::from_iter(data) }
}
impl<const N: usize> From<&mut [Direction; N]> for DirectionsSet {
fn from(data: &mut [Direction; N]) -> Self { Self::from_iter(data) }
}
impl<const N: usize> From<[Direction; N]> for DirectionsSet {
fn from(data: [Direction; N]) -> Self { Self::from_iter(data) }
}
impl From<&[Direction]> for DirectionsSet {
fn from(data: &[Direction]) -> Self { Self::from_iter(data) }
}
impl From<&mut [Direction]> for DirectionsSet {
fn from(data: &mut [Direction]) -> Self { Self::from_iter(data) }
}
impl FromIterator<Direction> for DirectionsSet {
#[expect(clippy::as_conversions)]
fn from_iter<I: IntoIterator<Item = Direction>>(iter: I) -> Self {
let mut retv = EMPTY;
for d in iter {
retv |= DIRECTIONS_FLAGS[usize::from(d as u8)];
}
Self(retv)
}
}
impl<'a> FromIterator<&'a Direction> for DirectionsSet {
fn from_iter<I: IntoIterator<Item = &'a Direction>>(iter: I) -> Self {
Self::from_iter(iter.into_iter().copied())
}
}
impl<'a> FromIterator<&'a mut Direction> for DirectionsSet {
fn from_iter<I: IntoIterator<Item = &'a mut Direction>>(iter: I) -> Self {
Self::from_iter(iter.into_iter().map(|d| *d))
}
}
impl<const N: usize> PartialEq<[Direction; N]> for DirectionsSet {
fn eq(&self, other: &[Direction; N]) -> bool { self.0 == DirectionsSet::from(other).0 }
}
impl<const N: usize> PartialEq<&[Direction; N]> for DirectionsSet {
fn eq(&self, other: &&[Direction; N]) -> bool { self.0 == DirectionsSet::from(*other).0 }
}
impl<const N: usize> PartialEq<&mut [Direction; N]> for DirectionsSet {
fn eq(&self, other: &&mut [Direction; N]) -> bool {
self.0 == DirectionsSet::from_iter(other.iter()).0
}
}
impl PartialEq<[Direction]> for DirectionsSet {
fn eq(&self, other: &[Direction]) -> bool { self.0 == DirectionsSet::from(other).0 }
}
impl PartialEq<&[Direction]> for DirectionsSet {
fn eq(&self, other: &&[Direction]) -> bool { self.0 == DirectionsSet::from(*other).0 }
}
impl PartialEq<&mut [Direction]> for DirectionsSet {
fn eq(&self, other: &&mut [Direction]) -> bool {
self.0 == DirectionsSet::from_iter(other.iter()).0
}
}
impl<const N: usize> PartialEq<DirectionsSet> for [Direction; N] {
fn eq(&self, other: &DirectionsSet) -> bool { DirectionsSet::from(self).0 == other.0 }
}
impl<const N: usize> PartialEq<DirectionsSet> for &[Direction; N] {
fn eq(&self, other: &DirectionsSet) -> bool {
DirectionsSet::from_iter(self.iter()).0 == other.0
}
}
impl<const N: usize> PartialEq<DirectionsSet> for &mut [Direction; N] {
fn eq(&self, other: &DirectionsSet) -> bool {
DirectionsSet::from_iter(self.iter()).0 == other.0
}
}
impl PartialEq<DirectionsSet> for [Direction] {
fn eq(&self, other: &DirectionsSet) -> bool { DirectionsSet::from(self).0 == other.0 }
}
impl PartialEq<DirectionsSet> for &[Direction] {
fn eq(&self, other: &DirectionsSet) -> bool {
DirectionsSet::from_iter(self.iter()).0 == other.0
}
}
impl PartialEq<DirectionsSet> for &mut [Direction] {
fn eq(&self, other: &DirectionsSet) -> bool {
DirectionsSet::from_iter(self.iter()).0 == other.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_calculates_opposite_direction() {
assert_eq!(Direction::Up.opposite(), Direction::Down);
assert_eq!(Direction::Down.opposite(), Direction::Up);
assert_eq!(Direction::Left.opposite(), Direction::Right);
assert_eq!(Direction::Right.opposite(), Direction::Left);
assert_eq!(Direction::NorthWest.opposite(), Direction::SouthEast);
assert_eq!(Direction::SouthEast.opposite(), Direction::NorthWest);
assert_eq!(Direction::NorthEast.opposite(), Direction::SouthWest);
assert_eq!(Direction::SouthWest.opposite(), Direction::NorthEast);
}
#[test]
fn it_is_empty_by_default() {
let neighbors = DirectionsSet::default();
assert!(neighbors.is_empty());
}
#[test]
fn it_can_be_constructed_from_and_compared_to_slice() {
let neighbors: DirectionsSet = [Direction::Up, Direction::Down].into();
assert_eq!(neighbors, [Direction::Up, Direction::Down]);
}
#[test]
fn test_order_in_source_slice_doesnt_matter() {
let neighbors1: DirectionsSet = [Direction::Up, Direction::Down].into();
let neighbors2: DirectionsSet = [Direction::Down, Direction::Up].into();
assert_eq!(neighbors1, neighbors2);
}
#[test]
fn it_can_be_iterated() {
let neighbors: DirectionsSet = [Direction::Up, Direction::Down].into();
let mut v: Vec<Direction> = Vec::new();
for d in neighbors {
v.push(d);
}
assert_eq!(v.len(), 2);
assert_eq!(v[0], Direction::Up);
assert_eq!(v[1], Direction::Down);
}
#[test]
fn it_iterates_in_clockwise_direction() {
let neighbors: DirectionsSet =
[Direction::NorthWest, Direction::Down, Direction::Up].into();
let mut v: Vec<Direction> = Vec::new();
for d in neighbors {
v.push(d);
}
assert_eq!(v.len(), 3);
assert_eq!(v[0], Direction::Up);
assert_eq!(v[1], Direction::Down);
assert_eq!(v[2], Direction::NorthWest);
}
}