adventofcode/src/coordinate_systems/kartesian/mod.rs
2024-12-18 21:59:56 +01:00

282 Zeilen
8,7 KiB
Rust

use log::debug;
use num::{integer::*, traits::*};
use std::{
fmt::{Debug, Display},
ops::*,
};
pub mod direction;
pub mod external_traits;
pub mod map;
pub mod maximum;
mod test;
pub mod traits;
pub use direction::*;
pub use map::*;
pub use maximum::*;
pub use traits::*;
pub type KD = KartesianDirection;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Kartesian<T>
where
T: Integer,
{
pub x: T,
pub y: T,
}
impl<T: Integer> Kartesian<T> {
pub const fn new(x: T, y: T) -> Self {
Self {
x,
y,
}
}
}
fn wrap<T: Integer + Rem + MaximumValue + From<u32> + Copy + MaximumValue + Display, const FACTOR: u32>(v: T, max: T) -> T {
if v < max {
v
} else if v < max * T::from(FACTOR) {
v % max
} else {
max - (T::MAX - v)
}
}
impl<T: Integer + Eq + Debug + CheckedBasicMath + Default + Copy> Kartesian<T> {
pub fn checked_add_max(self, rhs: Kartesian<T>, max: Kartesian<T>) -> Option<Kartesian<T>> {
let mut new = Kartesian::default();
new.x = match self.x.checked_add(&rhs.x) {
None => return None,
Some(x) => {
if x < T::default() || x >= max.x {
return None;
}
x
},
};
new.y = match self.y.checked_add(&rhs.y) {
None => return None,
Some(y) => {
if y < T::default() || y >= max.y {
return None;
}
y
},
};
Some(new)
}
pub fn checked_sub(self, rhs: Kartesian<T>) -> Option<Kartesian<T>> {
let mut new = Kartesian::default();
new.x = match self.x.checked_sub(&rhs.x) {
None => return None,
Some(x) => x,
};
new.y = match self.y.checked_sub(&rhs.y) {
None => return None,
Some(y) => y,
};
Some(new)
}
pub fn diff(self, rhs: Kartesian<T>) -> (Kartesian<T>, KartesianDirection) {
let mut relative = Kartesian::<T>::default();
let mut dir = KartesianDirection::None;
debug!("{:?} <> {:?}", self.x, rhs.x);
if self.x < rhs.x {
dir = dir.down();
relative.x = rhs.x - self.x;
} else if self.x > rhs.x {
dir = dir.up();
relative.x = self.x - rhs.x;
}
debug!("{:?} <> {:?}", self.x, rhs.x);
if self.y < rhs.y {
dir = dir.right();
relative.y = rhs.y - self.y;
} else if self.y > rhs.y {
dir = dir.left();
relative.y = self.y - rhs.y;
}
(relative, dir)
}
#[inline]
#[must_use]
pub fn move_dir(self, vector: Kartesian<T>, dir: KartesianDirection) -> Option<Kartesian<T>> {
let mut new = self;
match dir {
KartesianDirection::TopLeft | KartesianDirection::Top | KartesianDirection::TopRight => {
new.x = match new.x.checked_sub(&vector.x) {
None => return None,
Some(d) => d,
}
},
KartesianDirection::BottomLeft | KartesianDirection::Bottom | KartesianDirection::BottomRight => {
new.x = match new.x.checked_add(&vector.x) {
None => return None,
Some(d) => d,
}
},
_ => {},
}
match dir {
KartesianDirection::TopLeft | KartesianDirection::Left | KartesianDirection::BottomLeft => {
new.y = match new.y.checked_sub(&vector.y) {
None => return None,
Some(d) => d,
}
},
KartesianDirection::TopRight | KartesianDirection::Right | KartesianDirection::BottomRight => {
new.y = match new.y.checked_add(&vector.y) {
None => return None,
Some(d) => d,
}
},
_ => {},
}
Some(new)
}
#[inline]
#[must_use]
pub fn move_dir_max(self, vector: Kartesian<T>, dir: KartesianDirection, max: Kartesian<T>) -> Option<Kartesian<T>> {
match self.move_dir(vector, dir) {
None => None,
Some(new) => {
if new.x < max.x && new.y < max.y {
Some(new)
} else {
None
}
},
}
}
}
impl Kartesian<usize> {
pub fn get_value<T: Copy>(self, map: &Vec<Vec<T>>) -> Option<T> {
if map.len() > self.x {
if map[self.x].len() > self.y {
return Some(map[self.x][self.y]);
}
}
None
}
}
impl<T: Integer + Unsigned + CheckedBasicMath> Kartesian<T> {
#[must_use]
pub fn move_velocity(self, velocity: Velocity<T>, max: Kartesian<T>) -> Option<Kartesian<T>> {
if velocity.direction == KD::None {
return Some(self);
}
Some(Kartesian {
x: match velocity.direction {
KD::Left | KD::Right => self.x,
KD::TopLeft | KD::Top | KD::TopRight => match self.x.checked_sub(&velocity.speed.x) {
None => return None,
Some(x) => x,
},
KD::BottomLeft | KD::Bottom | KD::BottomRight => {
let x = self.x + velocity.speed.x;
if x >= max.x {
return None;
}
x
},
_ => unreachable!(),
},
y: match velocity.direction {
KD::Top | KD::Bottom => self.y,
KD::TopLeft | KD::Left | KD::BottomLeft => match self.y.checked_sub(&velocity.speed.y) {
None => return None,
Some(y) => y,
},
KD::TopRight | KD::Right | KD::BottomRight => {
let y = self.y + velocity.speed.y;
if y >= max.y {
return None;
}
y
},
_ => unreachable!(),
},
})
}
}
impl<T: Integer + From<u16> + Copy + Add + Roots + Pow<T, Output = T>> Kartesian<T> {
pub fn distance(self, other: Self) -> T {
let power = T::from(2);
let x = if self.x > other.x {
self.x - other.x
} else {
other.x - self.x
};
let y = if self.y > other.y {
self.y - other.y
} else {
other.y - self.y
};
(x.pow(power) + y.pow(power)).sqrt()
}
}
impl<T: Integer + Display + Unsigned + WrappingAdd + WrappingSub + MaximumValue + From<u32> + Copy> Kartesian<T> {
pub fn wrapping_move_velocity(self, velocity: Velocity<T>, max: Kartesian<T>) -> Kartesian<T> {
Kartesian {
x: match velocity.direction {
KartesianDirection::TopLeft | KartesianDirection::Top | KartesianDirection::TopRight => {
if self.x < velocity.speed.x {
max.x - (velocity.speed.x - self.x)
} else {
self.x - velocity.speed.x
}
},
KartesianDirection::Left | KartesianDirection::None | KartesianDirection::Right => self.x,
KartesianDirection::BottomLeft | KartesianDirection::Bottom | KartesianDirection::BottomRight => (self.x + velocity.speed.x) % max.x,
},
y: match velocity.direction {
KartesianDirection::TopLeft | KartesianDirection::Left | KartesianDirection::BottomLeft => {
if self.y < velocity.speed.y {
max.y - (velocity.speed.y - self.y)
} else {
self.y - velocity.speed.y
}
},
KartesianDirection::Top | KartesianDirection::None | KartesianDirection::Bottom => self.y,
KartesianDirection::TopRight | KartesianDirection::Right | KartesianDirection::BottomRight => (self.y + velocity.speed.y) % max.y,
},
}
}
}
pub struct KartesianIterator {
i: u8,
diagonal: bool,
none: bool,
straight: bool,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Velocity<T: Integer + Unsigned> {
speed: Kartesian<T>,
direction: KartesianDirection,
}
impl<T: Integer + Unsigned> Velocity<T> {
pub fn new(x: T, y: T, dir: KartesianDirection) -> Self {
Velocity {
speed: Kartesian {
x,
y,
},
direction: dir,
}
}
}