#![feature(adt_const_params)]
#![feature(const_trait_impl)]
#![feature(const_cmp)]
#![feature(const_convert)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
#![feature(const_try)]
#![allow(incomplete_features)]
#![warn(clippy::pedantic)]
use std::{
fmt,
ops::{Add, AddAssign, Deref, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Immutable<T>(T);
impl<T> Immutable<T> {
pub fn new(value: T) -> Self {
Immutable(value)
}
}
impl<T> const Deref for Immutable<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
One = 0,
Two = 1,
Three = 2,
Four = 3,
Five = 4,
Six = 6,
}
#[allow(non_upper_case_globals)]
impl Direction {
pub const QMinus: Self = Self::Four;
pub const QPlus: Self = Self::One;
pub const RMinus: Self = Self::Two;
pub const RPlus: Self = Self::Five;
pub const SMinus: Self = Self::Six;
pub const SPlus: Self = Self::Three;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OffsetSystem {
OddR,
EvenR,
OddQ,
EvenQ,
}
pub enum Row {
Even,
Odd,
}
impl<const S: OffsetSystem> const From<OffsetCoordinates<S>> for Row {
fn from(OffsetCoordinates { row, .. }: OffsetCoordinates<S>) -> Self {
if row & 1 == 0 {
Self::Even
} else {
Self::Odd
}
}
}
pub enum Col {
Even,
Odd,
}
impl<const S: OffsetSystem> const From<OffsetCoordinates<S>> for Col {
fn from(OffsetCoordinates { col, .. }: OffsetCoordinates<S>) -> Self {
if col & 1 == 0 {
Self::Even
} else {
Self::Odd
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct OffsetCoordinates<const S: OffsetSystem> {
pub col: isize,
pub row: isize,
}
impl<const S: OffsetSystem> OffsetCoordinates<S> {
#[must_use]
pub const fn new(col: isize, row: isize) -> Self {
Self { col, row }
}
}
impl OffsetCoordinates<{ OffsetSystem::OddR }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
let parity = Row::from(*self);
*self + Self::from((parity, direction))
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let a = AxialCoordinates::from(*self);
let b = AxialCoordinates::from(other);
a.manhattan_distance(b)
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl OffsetCoordinates<{ OffsetSystem::EvenR }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
let parity = Row::from(*self);
*self + Self::from((parity, direction))
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let a = AxialCoordinates::from(*self);
let b = AxialCoordinates::from(other);
a.manhattan_distance(b)
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl OffsetCoordinates<{ OffsetSystem::OddQ }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
let parity = Col::from(*self);
*self + Self::from((parity, direction))
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let a = AxialCoordinates::from(*self);
let b = AxialCoordinates::from(other);
a.manhattan_distance(b)
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl OffsetCoordinates<{ OffsetSystem::EvenQ }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
let parity = Col::from(*self);
*self + Self::from((parity, direction))
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let a = AxialCoordinates::from(*self);
let b = AxialCoordinates::from(other);
a.manhattan_distance(b)
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl const From<AxialCoordinates> for OffsetCoordinates<{ OffsetSystem::OddR }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = q + (r - (r & 1)) / 2;
let row = r;
Self { col, row }
}
}
impl const From<AxialCoordinates> for OffsetCoordinates<{ OffsetSystem::EvenR }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = q + (r + (r & 1)) / 2;
let row = r;
Self { col, row }
}
}
impl const From<AxialCoordinates> for OffsetCoordinates<{ OffsetSystem::OddQ }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = q;
let row = r + (q - (q & 1)) / 2;
Self { col, row }
}
}
impl const From<AxialCoordinates> for OffsetCoordinates<{ OffsetSystem::EvenQ }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = q;
let row = r + (q + (q & 1)) / 2;
Self { col, row }
}
}
impl const From<CubeCoordinates> for OffsetCoordinates<{ OffsetSystem::OddR }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = *q + (*r - (*r & 1)) / 2;
let row = *r;
Self { col, row }
}
}
impl const From<CubeCoordinates> for OffsetCoordinates<{ OffsetSystem::EvenR }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = *q + (*r + (*r & 1)) / 2;
let row = *r;
Self { col, row }
}
}
impl const From<CubeCoordinates> for OffsetCoordinates<{ OffsetSystem::OddQ }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = *q;
let row = *r + (*q - (*q & 1)) / 2;
Self { col, row }
}
}
impl const From<CubeCoordinates> for OffsetCoordinates<{ OffsetSystem::EvenQ }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = *q;
let row = *r + (*q + (*q & 1)) / 2;
Self { col, row }
}
}
impl const From<(Row, Direction)> for OffsetCoordinates<{ OffsetSystem::OddR }> {
fn from((row, direction): (Row, Direction)) -> Self {
match row {
Row::Even => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 0, row: -1 },
Direction::Three => Self { col: -1, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: -1, row: 1 },
Direction::Six => Self { col: 0, row: 1 },
},
Row::Odd => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 1, row: -1 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: 0, row: 1 },
Direction::Six => Self { col: 1, row: 1 },
},
}
}
}
impl const From<(Row, Direction)> for OffsetCoordinates<{ OffsetSystem::EvenR }> {
fn from((row, direction): (Row, Direction)) -> Self {
match row {
Row::Odd => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 0, row: -1 },
Direction::Three => Self { col: -1, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: -1, row: 1 },
Direction::Six => Self { col: 0, row: 1 },
},
Row::Even => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 1, row: -1 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: 0, row: 1 },
Direction::Six => Self { col: 1, row: 1 },
},
}
}
}
impl const From<(Col, Direction)> for OffsetCoordinates<{ OffsetSystem::OddQ }> {
fn from((col, direction): (Col, Direction)) -> Self {
match col {
Col::Even => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 1, row: -1 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: -1 },
Direction::Five => Self { col: -1, row: 0 },
Direction::Six => Self { col: 0, row: 1 },
},
Col::Odd => match direction {
Direction::One => Self { col: 1, row: 1 },
Direction::Two => Self { col: 1, row: 0 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: -1, row: 1 },
Direction::Six => Self { col: 0, row: 1 },
},
}
}
}
impl const From<(Col, Direction)> for OffsetCoordinates<{ OffsetSystem::EvenQ }> {
fn from((col, direction): (Col, Direction)) -> Self {
match col {
Col::Odd => match direction {
Direction::One => Self { col: 1, row: 0 },
Direction::Two => Self { col: 1, row: -1 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: -1 },
Direction::Five => Self { col: -1, row: 0 },
Direction::Six => Self { col: 0, row: 1 },
},
Col::Even => match direction {
Direction::One => Self { col: 1, row: 1 },
Direction::Two => Self { col: 1, row: 0 },
Direction::Three => Self { col: 0, row: -1 },
Direction::Four => Self { col: -1, row: 0 },
Direction::Five => Self { col: -1, row: 1 },
Direction::Six => Self { col: 0, row: 1 },
},
}
}
}
impl<const S: OffsetSystem> const Add for OffsetCoordinates<S> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
col: self.col + other.col,
row: self.row + other.row,
}
}
}
impl<const S: OffsetSystem> const Sub for OffsetCoordinates<S> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
col: self.col - other.col,
row: self.row - other.row,
}
}
}
impl<const S: OffsetSystem> const AddAssign for OffsetCoordinates<S> {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl<const S: OffsetSystem> const SubAssign for OffsetCoordinates<S> {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl<const S: OffsetSystem> const Mul<isize> for OffsetCoordinates<S> {
type Output = Self;
fn mul(self, rhs: isize) -> Self {
Self {
col: self.col * rhs,
row: self.row * rhs,
}
}
}
impl<const S: OffsetSystem> const Div<isize> for OffsetCoordinates<S> {
type Output = Self;
fn div(self, rhs: isize) -> Self {
Self {
col: self.col / rhs,
row: self.row / rhs,
}
}
}
impl<const S: OffsetSystem> const MulAssign<isize> for OffsetCoordinates<S> {
fn mul_assign(&mut self, rhs: isize) {
*self = *self * rhs;
}
}
impl<const S: OffsetSystem> const DivAssign<isize> for OffsetCoordinates<S> {
fn div_assign(&mut self, rhs: isize) {
*self = *self / rhs;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rotation {
Zero = 0,
One = 1,
Two = 2,
Three = 3,
Four = 4,
Five = 5,
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Eleven = 11,
}
impl const Add for Rotation {
type Output = Self;
fn add(self, other: Self) -> Self::Output {
let v = (self as u8 + other as u8) % 12;
Self::from(v)
}
}
impl const Sub for Rotation {
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
let v = (self as u8 - other as u8) % 12;
Self::from(v)
}
}
impl const AddAssign for Rotation {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl const SubAssign for Rotation {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl const From<u8> for Rotation {
fn from(x: u8) -> Self {
match x {
0 => Self::Zero,
1 => Self::One,
2 => Self::Two,
3 => Self::Three,
4 => Self::Four,
5 => Self::Five,
6 => Self::Six,
7 => Self::Seven,
8 => Self::Eight,
9 => Self::Nine,
10 => Self::Ten,
11 => Self::Eleven,
_ => unreachable!(),
}
}
}
impl const TryFrom<i16> for Rotation {
type Error = ();
fn try_from(int: i16) -> Result<Self, Self::Error> {
match int {
-360 | 360 => Ok(Self::Zero),
-300 => Ok(Self::One),
-240 => Ok(Self::Two),
-180 => Ok(Self::Three),
-120 => Ok(Self::Four),
-60 => Ok(Self::Five),
0 => Ok(Self::Six),
60 => Ok(Self::Seven),
120 => Ok(Self::Eight),
180 => Ok(Self::Nine),
240 => Ok(Self::Ten),
300 => Ok(Self::Eleven),
_ => Err(()),
}
}
}
impl const TryFrom<f32> for Rotation {
type Error = ();
fn try_from(float: f32) -> Result<Self, Self::Error> {
use std::f32::consts::PI;
const P1: f32 = (1. / 3.) * PI;
const P2: f32 = (2. / 3.) * PI;
const P3: f32 = PI;
const P4: f32 = (4. / 3.) * PI;
const P5: f32 = (5. / 3.) * PI;
const P6: f32 = (6. / 3.) * PI;
const NP1: f32 = -P1;
const NP2: f32 = -P2;
const NP3: f32 = -P3;
const NP4: f32 = -P4;
const NP5: f32 = -P5;
const NP6: f32 = -P6;
match float {
x if x == NP6 || x == P6 => Ok(Self::Zero),
x if x == NP5 => Ok(Self::One),
x if x == NP4 => Ok(Self::Two),
x if x == NP3 => Ok(Self::Three),
x if x == NP2 => Ok(Self::Four),
x if x == NP1 => Ok(Self::Five),
x if x == 0. => Ok(Self::Six),
x if x == P1 => Ok(Self::Seven),
x if x == P2 => Ok(Self::Eight),
x if x == P3 => Ok(Self::Nine),
x if x == P4 => Ok(Self::Ten),
x if x == P5 => Ok(Self::Eleven),
_ => Err(()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidCubeCoordinate(pub CubeCoordinates);
impl fmt::Display for InvalidCubeCoordinate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "The cube coordiantes of {:?} are invalid", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CubeAxis {
Q,
R,
S,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct CubeCoordinates {
pub q: Immutable<isize>,
pub r: Immutable<isize>,
pub s: Immutable<isize>,
}
impl CubeCoordinates {
pub const fn new(q: isize, r: isize, s: isize) -> Result<Self, InvalidCubeCoordinate> {
if q + r + s == 0 {
Ok(Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(s),
})
} else {
Err(InvalidCubeCoordinate(Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(s),
}))
}
}
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
*self + Self::from(direction)
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
(((*self.q - *other.q).abs() + (*self.r - *other.r).abs() + (*self.s - *other.s).abs()) / 2)
as usize
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let range = isize::try_from(range).unwrap();
let mut results = Vec::new();
for q in -range..=range {
for r in std::cmp::max(-range, -q - range)..=std::cmp::min(range, -q + range) {
let s = -q - r;
results.push(
*self
+ Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(s),
},
);
}
}
results
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let vec = *self - center;
let rotated = match rotation {
Rotation::Zero | Rotation::Six => vec,
Rotation::One | Rotation::Eleven => Self {
q: Immutable(-*vec.r),
r: Immutable(-*vec.s),
s: Immutable(-*vec.q),
},
Rotation::Two | Rotation::Ten => Self {
q: Immutable(*vec.s),
r: Immutable(*vec.q),
s: Immutable(*vec.r),
},
Rotation::Three | Rotation::Nine => Self {
q: Immutable(-*vec.q),
r: Immutable(-*vec.r),
s: Immutable(-*vec.s),
},
Rotation::Four | Rotation::Eight => Self {
q: Immutable(*vec.r),
r: Immutable(*vec.s),
s: Immutable(*vec.q),
},
Rotation::Five | Rotation::Seven => Self {
q: Immutable(-*vec.s),
r: Immutable(-*vec.q),
s: Immutable(-*vec.r),
},
};
rotated + center
}
pub const fn try_div(&self, rhs: isize) -> Result<Self, InvalidCubeCoordinate> {
let result = Self {
q: Immutable(*self.q / rhs),
r: Immutable(*self.r / rhs),
s: Immutable(*self.s / rhs),
};
if *result.q + *result.r + *result.s == 0 {
Ok(result)
} else {
Err(InvalidCubeCoordinate(result))
}
}
pub const fn try_div_assign(&mut self, rhs: isize) -> Result<(), InvalidCubeCoordinate> {
let result = self.try_div(rhs)?;
*self = result;
Ok(())
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
match axis {
CubeAxis::Q => Self {
q: self.q,
r: self.s,
s: self.r,
},
CubeAxis::R => Self {
q: self.s,
r: self.r,
s: self.q,
},
CubeAxis::S => Self {
q: self.r,
r: self.q,
s: self.s,
},
}
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::OddR }>> for CubeCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::OddR }>) -> Self {
let q = col - (row - (row & 1)) / 2;
let r = row;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::EvenR }>> for CubeCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::EvenR }>) -> Self {
let q = col - (row + (row & 1)) / 2;
let r = row;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::OddQ }>> for CubeCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::OddQ }>) -> Self {
let q = col;
let r = row - (col - (col & 1)) / 2;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::EvenQ }>> for CubeCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::EvenQ }>) -> Self {
let q = col;
let r = row - (col + (col & 1)) / 2;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<AxialCoordinates> for CubeCoordinates {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<DoubledCoordinates<{ DoubledSystem::Height }>> for CubeCoordinates {
fn from(
DoubledCoordinates { col, row }: DoubledCoordinates<{ DoubledSystem::Height }>,
) -> Self {
let q = *col;
let r = (*row - *col) / 2;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<DoubledCoordinates<{ DoubledSystem::Width }>> for CubeCoordinates {
fn from(DoubledCoordinates { col, row }: DoubledCoordinates<{ DoubledSystem::Width }>) -> Self {
let q = (*col - *row) / 2;
let r = *row;
Self {
q: Immutable(q),
r: Immutable(r),
s: Immutable(-q - r),
}
}
}
impl const From<Direction> for CubeCoordinates {
fn from(direction: Direction) -> Self {
match direction {
Direction::One => Self {
q: Immutable(1),
r: Immutable(0),
s: Immutable(-1),
},
Direction::Two => Self {
q: Immutable(1),
r: Immutable(-1),
s: Immutable(0),
},
Direction::Three => Self {
q: Immutable(0),
r: Immutable(-1),
s: Immutable(1),
},
Direction::Four => Self {
q: Immutable(-1),
r: Immutable(0),
s: Immutable(1),
},
Direction::Five => Self {
q: Immutable(-1),
r: Immutable(1),
s: Immutable(0),
},
Direction::Six => Self {
q: Immutable(0),
r: Immutable(1),
s: Immutable(-1),
},
}
}
}
impl const Add for CubeCoordinates {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
q: Immutable(*self.q + *other.q),
r: Immutable(*self.r + *other.r),
s: Immutable(*self.s + *other.s),
}
}
}
impl const Sub for CubeCoordinates {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
q: Immutable(*self.q - *other.q),
r: Immutable(*self.r - *other.r),
s: Immutable(*self.s - *other.s),
}
}
}
impl const AddAssign for CubeCoordinates {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl const SubAssign for CubeCoordinates {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl const Mul<isize> for CubeCoordinates {
type Output = Self;
fn mul(self, rhs: isize) -> Self {
Self {
q: Immutable(*self.q * rhs),
r: Immutable(*self.r * rhs),
s: Immutable(*self.s * rhs),
}
}
}
impl const MulAssign<isize> for CubeCoordinates {
fn mul_assign(&mut self, rhs: isize) {
*self = *self * rhs;
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct AxialCoordinates {
pub q: isize,
pub r: isize,
}
impl AxialCoordinates {
#[must_use]
pub const fn new(q: isize, r: isize) -> Self {
Self { q, r }
}
#[must_use]
pub const fn s(&self) -> isize {
-self.q - self.r
}
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
*self + Self::from(direction)
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
(((self.q - other.q).abs()
+ (self.r - other.r).abs()
+ (self.q + self.r - other.q - other.r).abs())
/ 2) as usize
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let range = isize::try_from(range).unwrap();
let mut results = Vec::new();
for q in -range..=range {
for r in std::cmp::max(-range, -q - range)..=std::cmp::min(range, -q + range) {
results.push(*self + Self { q, r });
}
}
results
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let vec = *self - center;
let rotated = match rotation {
Rotation::Zero | Rotation::Six => vec,
Rotation::One | Rotation::Eleven => Self {
q: -vec.r,
r: -vec.s(),
},
Rotation::Two | Rotation::Ten => Self {
q: vec.s(),
r: vec.q,
},
Rotation::Three | Rotation::Nine => Self {
q: -vec.q,
r: -vec.r,
},
Rotation::Four | Rotation::Eight => Self {
q: vec.r,
r: vec.s(),
},
Rotation::Five | Rotation::Seven => Self {
q: -vec.s(),
r: -vec.q,
},
};
rotated + center
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl const From<CubeCoordinates> for AxialCoordinates {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
Self { q: *q, r: *r }
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::OddR }>> for AxialCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::OddR }>) -> Self {
let q = col - (row - (row & 1)) / 2;
let r = row;
Self { q, r }
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::EvenR }>> for AxialCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::EvenR }>) -> Self {
let q = col - (row + (row & 1)) / 2;
let r = row;
Self { q, r }
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::OddQ }>> for AxialCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::OddQ }>) -> Self {
let q = col;
let r = row - (col - (col & 1)) / 2;
Self { q, r }
}
}
impl const From<OffsetCoordinates<{ OffsetSystem::EvenQ }>> for AxialCoordinates {
fn from(OffsetCoordinates { col, row }: OffsetCoordinates<{ OffsetSystem::EvenQ }>) -> Self {
let q = col;
let r = row - (col + (col & 1)) / 2;
Self { q, r }
}
}
impl const From<DoubledCoordinates<{ DoubledSystem::Height }>> for AxialCoordinates {
fn from(
DoubledCoordinates { col, row }: DoubledCoordinates<{ DoubledSystem::Height }>,
) -> Self {
let q = *col;
let r = (*row - *col) / 2;
Self { q, r }
}
}
impl const From<DoubledCoordinates<{ DoubledSystem::Width }>> for AxialCoordinates {
fn from(DoubledCoordinates { col, row }: DoubledCoordinates<{ DoubledSystem::Width }>) -> Self {
let q = (*col - *row) / 2;
let r = *row;
Self { q, r }
}
}
impl const From<Direction> for AxialCoordinates {
fn from(direction: Direction) -> Self {
match direction {
Direction::One => Self { q: 1, r: 0 },
Direction::Two => Self { q: 1, r: -1 },
Direction::Three => Self { q: 0, r: -1 },
Direction::Four => Self { q: -1, r: 0 },
Direction::Five => Self { q: -1, r: 1 },
Direction::Six => Self { q: 0, r: 1 },
}
}
}
impl const Add for AxialCoordinates {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
q: self.q + other.q,
r: self.r + other.r,
}
}
}
impl const Sub for AxialCoordinates {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
q: self.q - other.q,
r: self.r - other.r,
}
}
}
impl const AddAssign for AxialCoordinates {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl const SubAssign for AxialCoordinates {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl const Mul<isize> for AxialCoordinates {
type Output = Self;
fn mul(self, rhs: isize) -> Self {
Self {
q: self.q * rhs,
r: self.r * rhs,
}
}
}
impl const Div<isize> for AxialCoordinates {
type Output = Self;
fn div(self, rhs: isize) -> Self {
Self {
q: self.q / rhs,
r: self.r / rhs,
}
}
}
impl const MulAssign<isize> for AxialCoordinates {
fn mul_assign(&mut self, rhs: isize) {
*self = *self * rhs;
}
}
impl const DivAssign<isize> for AxialCoordinates {
fn div_assign(&mut self, rhs: isize) {
*self = *self / rhs;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DoubledSystem {
Width,
Height,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct DoubledCoordinates<const S: DoubledSystem> {
pub col: Immutable<isize>,
pub row: Immutable<isize>,
}
impl<const S: DoubledSystem> DoubledCoordinates<S> {
pub const fn new(col: isize, row: isize) -> Result<Self, &'static str> {
if (col + row) % 2 == 0 {
Ok(Self {
col: Immutable(col),
row: Immutable(row),
})
} else {
Err("(q + r) % 2 != 0")
}
}
}
impl DoubledCoordinates<{ DoubledSystem::Height }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
*self + Self::from(direction)
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let dcol = (*self.col - *other.col).abs();
let drow = (*self.row - *other.row).abs();
(drow + std::cmp::max(0, (dcol - drow) / 2)) as usize
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl DoubledCoordinates<{ DoubledSystem::Width }> {
#[must_use]
pub const fn neighbor(&self, direction: Direction) -> Self {
*self + Self::from(direction)
}
#[must_use]
pub const fn manhattan_distance(&self, other: Self) -> usize {
let dcol = (*self.col - *other.col).abs();
let drow = (*self.row - *other.row).abs();
(dcol + std::cmp::max(0, (drow - dcol) / 2)) as usize
}
#[must_use]
pub fn coordinate_range(&self, range: usize) -> Vec<Self> {
let axial = AxialCoordinates::from(*self);
let axial_range = axial.coordinate_range(range);
axial_range.into_iter().map(Self::from).collect()
}
#[must_use]
pub const fn rotate(&self, center: Self, rotation: Rotation) -> Self {
let axial_self = AxialCoordinates::from(*self);
let axial_center = AxialCoordinates::from(center);
axial_self.rotate(axial_center, rotation).into()
}
#[must_use]
pub const fn reflect(&self, axis: CubeAxis) -> Self {
let cube = CubeCoordinates::from(*self);
let reflected = cube.reflect(axis);
Self::from(reflected)
}
}
impl const From<AxialCoordinates> for DoubledCoordinates<{ DoubledSystem::Height }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = q;
let row = 2 * r + q;
Self {
col: Immutable(col),
row: Immutable(row),
}
}
}
impl const From<AxialCoordinates> for DoubledCoordinates<{ DoubledSystem::Width }> {
fn from(AxialCoordinates { q, r }: AxialCoordinates) -> Self {
let col = 2 * q + r;
let row = r;
Self {
col: Immutable(col),
row: Immutable(row),
}
}
}
impl const From<CubeCoordinates> for DoubledCoordinates<{ DoubledSystem::Height }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = *q;
let row = 2 * *r + *q;
Self {
col: Immutable(col),
row: Immutable(row),
}
}
}
impl const From<CubeCoordinates> for DoubledCoordinates<{ DoubledSystem::Width }> {
fn from(CubeCoordinates { q, r, s: _ }: CubeCoordinates) -> Self {
let col = 2 * *q + *r;
let row = *r;
Self {
col: Immutable(col),
row: Immutable(row),
}
}
}
impl const From<Direction> for DoubledCoordinates<{ DoubledSystem::Height }> {
fn from(direction: Direction) -> Self {
match direction {
Direction::One => Self {
col: Immutable(1),
row: Immutable(1),
},
Direction::Two => Self {
col: Immutable(1),
row: Immutable(-1),
},
Direction::Three => Self {
col: Immutable(0),
row: Immutable(-2),
},
Direction::Four => Self {
col: Immutable(-1),
row: Immutable(-1),
},
Direction::Five => Self {
col: Immutable(-1),
row: Immutable(1),
},
Direction::Six => Self {
col: Immutable(0),
row: Immutable(2),
},
}
}
}
impl const From<Direction> for DoubledCoordinates<{ DoubledSystem::Width }> {
fn from(direction: Direction) -> Self {
match direction {
Direction::One => Self {
col: Immutable(2),
row: Immutable(0),
},
Direction::Two => Self {
col: Immutable(1),
row: Immutable(-1),
},
Direction::Three => Self {
col: Immutable(-1),
row: Immutable(-1),
},
Direction::Four => Self {
col: Immutable(-2),
row: Immutable(0),
},
Direction::Five => Self {
col: Immutable(-1),
row: Immutable(1),
},
Direction::Six => Self {
col: Immutable(1),
row: Immutable(1),
},
}
}
}
impl<const S: DoubledSystem> const Add for DoubledCoordinates<S> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
col: Immutable(*self.col + *other.col),
row: Immutable(*self.row + *other.row),
}
}
}
impl<const S: DoubledSystem> const Sub for DoubledCoordinates<S> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
col: Immutable(*self.col - *other.col),
row: Immutable(*self.row - *other.row),
}
}
}
impl<const S: DoubledSystem> const AddAssign for DoubledCoordinates<S> {
fn add_assign(&mut self, other: Self) {
*self = *self + other;
}
}
impl<const S: DoubledSystem> const SubAssign for DoubledCoordinates<S> {
fn sub_assign(&mut self, other: Self) {
*self = *self - other;
}
}
impl<const S: DoubledSystem> const Mul<isize> for DoubledCoordinates<S> {
type Output = Self;
fn mul(self, rhs: isize) -> Self {
Self {
col: Immutable(*self.col * rhs),
row: Immutable(*self.row * rhs),
}
}
}
impl<const S: DoubledSystem> const MulAssign<isize> for DoubledCoordinates<S> {
fn mul_assign(&mut self, rhs: isize) {
*self = *self * rhs;
}
}
#[cfg(test)]
mod tests {
use super::*;
}