This commit is contained in:
Ruediger Ludwig 2023-01-24 10:13:28 +01:00
commit 284f099d3e
22 changed files with 1717 additions and 0 deletions

312
src/common/area.rs Normal file
View file

@ -0,0 +1,312 @@
#![allow(dead_code)]
use std::fmt::Display;
use super::{number::Number, pos::Pos};
#[derive(Debug, Clone, Copy, Default)]
pub struct Area<T>
where
T: Number,
{
lower_left: Pos<T>,
upper_right: Pos<T>,
}
impl<T> Area<T>
where
T: Number + Ord,
{
pub fn new(p1: Pos<T>, p2: Pos<T>) -> Area<T> {
Area {
lower_left: p1.min_components(&p2),
upper_right: p1.max_components(&p2),
}
}
pub fn extend(&self, pos: Pos<T>) -> Area<T> {
if self.contains(pos) {
return *self;
}
Area {
lower_left: self.lower_left.min_components(&pos),
upper_right: self.upper_right.max_components(&pos),
}
}
pub fn get_lower_left(&self) -> Pos<T> {
self.lower_left
}
pub fn get_upper_right(&self) -> Pos<T> {
self.upper_right
}
pub fn contains(&self, pos: Pos<T>) -> bool {
self.lower_left.x() >= pos.x()
&& pos.x() >= self.upper_right.x()
&& self.lower_left.y() >= pos.y()
&& pos.y() >= self.upper_right.y()
}
}
impl<'a, T> Area<T>
where
T: Number + Ord + 'a,
{
pub fn from_iterator<I>(mut iter: I) -> Option<Self>
where
I: Iterator<Item = &'a Pos<T>>,
{
let first = *iter.next()?;
let (upper, lower) = iter.fold((first, first), |(mx, mn), p| {
(mx.max_components(&p), mn.min_components(&p))
});
Some(Area::new(lower, upper))
}
}
impl<T> Area<T>
where
T: Number,
{
pub fn width(&self) -> T {
self.upper_right.x() - self.lower_left.x() + T::ONE
}
#[allow(dead_code)]
pub fn height(&self) -> T {
self.upper_right.y() - self.lower_left.y() + T::ONE
}
}
impl<T> Area<T>
where
T: Number,
{
#[allow(dead_code)]
pub fn area(&self) -> T {
self.width() * self.height()
}
}
impl<T> Display for Area<T>
where
T: Number + Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}-{}]", self.lower_left, self.upper_right)
}
}
impl<T> Area<T>
where
T: Number,
{
pub fn cells(&self, ascending: bool) -> CellIterator<'_, T> {
CellIterator::new(self, ascending)
}
pub fn rows(&self, ascending: bool) -> RowIterator<'_, T> {
RowIterator::new(self, ascending)
}
}
#[derive(Debug)]
pub struct RowIterator<'a, T>
where
T: Number,
{
area: &'a Area<T>,
row: T,
ascending: bool,
}
impl<'a, T> RowIterator<'a, T>
where
T: Number,
{
fn new(area: &'a Area<T>, ascending: bool) -> RowIterator<'a, T> {
RowIterator {
area,
row: if ascending {
area.lower_left.y()
} else {
area.upper_right.y()
},
ascending,
}
}
}
impl<'a, T> Iterator for RowIterator<'a, T>
where
T: Number,
{
type Item = Row<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if (self.ascending && self.row <= self.area.upper_right.y())
|| (!self.ascending && self.row >= self.area.lower_left.y())
{
let row = Row {
area: self.area,
row: self.row,
};
if self.ascending {
self.row += T::ONE;
} else {
self.row -= T::ONE;
}
Some(row)
} else {
None
}
}
}
#[derive(Debug)]
pub struct Row<'a, T>
where
T: Number,
{
area: &'a Area<T>,
row: T,
}
impl<'a, T> Row<'a, T>
where
T: Number,
{
pub fn cols(&self, ascending: bool) -> ColIterator<'_, T> {
ColIterator {
area: self.area,
row: self.row,
col: if ascending {
self.area.lower_left.x()
} else {
self.area.upper_right.x()
},
ascending,
}
}
}
#[derive(Debug)]
pub struct ColIterator<'a, T>
where
T: Number,
{
area: &'a Area<T>,
row: T,
col: T,
ascending: bool,
}
impl<'a, T> Iterator for ColIterator<'a, T>
where
T: Number,
{
type Item = Pos<T>;
fn next(&mut self) -> Option<Self::Item> {
if (self.ascending && self.col <= self.area.upper_right.x())
|| (!self.ascending && self.col >= self.area.lower_left.x())
{
let pos = Pos::new(self.col, self.row);
if self.ascending {
self.col += T::ONE
} else {
self.col -= T::ONE
};
Some(pos)
} else {
None
}
}
}
#[derive(Debug)]
pub struct CellIterator<'a, T>
where
T: Number,
{
area: &'a Area<T>,
row: T,
col: T,
ascending: bool,
}
impl<'a, T> CellIterator<'a, T>
where
T: Number,
{
pub fn new(area: &'a Area<T>, ascending: bool) -> CellIterator<'a, T> {
let (col, row) = if ascending {
(area.lower_left.x(), area.lower_left.y())
} else {
(area.upper_right.x(), area.upper_right.y())
};
CellIterator {
area,
row,
col,
ascending,
}
}
}
impl<'a, T> Iterator for CellIterator<'a, T>
where
T: Number,
{
type Item = Pos<T>;
fn next(&mut self) -> Option<Self::Item> {
if (self.ascending && self.row <= self.area.upper_right.y())
|| (!self.ascending && self.row >= self.area.lower_left.y())
{
let pos = Pos::new(self.col, self.row);
if self.ascending {
self.col += T::ONE;
if self.col > self.area.upper_right.x() {
self.row += T::ONE;
self.col = self.area.lower_left.x();
}
} else {
self.col -= T::ONE;
if self.col < self.area.lower_left.x() {
self.row -= T::ONE;
self.col = self.area.upper_right.x();
}
}
Some(pos)
} else {
None
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_cell_iterator() {
let area = Area::new(Pos::new(-1, -1), Pos::new(1, 1));
let result = area.cells(true).collect::<Vec<_>>();
let expected = vec![
Pos::new(-1, -1),
Pos::new(0, -1),
Pos::new(1, -1),
Pos::new(-1, 0),
Pos::new(0, 0),
Pos::new(1, 0),
Pos::new(-1, 1),
Pos::new(0, 1),
Pos::new(1, 1),
];
assert_eq!(result, expected);
}
}

156
src/common/direction.rs Normal file
View file

@ -0,0 +1,156 @@
use super::{pos::Pos, turn::Turn};
use std::{fmt::Display, ops::Add};
use Direction::*;
use Turn::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Direction {
East,
North,
West,
South,
}
impl Direction {
pub fn as_pos(&self) -> Pos<i32> {
match *self {
East => Pos::new(1, 0),
North => Pos::new(0, 1),
West => Pos::new(-1, 0),
South => Pos::new(0, -1),
}
}
pub fn is_perpendicular(&self, other: &Direction) -> bool {
match *self {
East => *other != East && *other != West,
North => *other != North && *other != South,
West => *other != East && *other != West,
South => *other != North && *other != South,
}
}
pub fn get_turn(&self, toward: Direction) -> Turn {
if *self == toward {
Forward
} else if toward == self.turn_left() {
Left
} else if toward == self.turn_right() {
Right
} else {
Back
}
}
pub fn turn(&self, turn: Turn) -> Direction {
match turn {
Left => self.turn_left(),
Right => self.turn_right(),
Back => self.turn_back(),
Forward => *self,
}
}
pub fn turn_right(&self) -> Direction {
match *self {
East => South,
North => East,
West => North,
South => West,
}
}
pub fn turn_left(&self) -> Direction {
match *self {
East => North,
North => West,
West => South,
South => East,
}
}
pub fn turn_back(&self) -> Direction {
match *self {
East => West,
North => South,
West => East,
South => North,
}
}
}
impl Display for Direction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Direction::East => write!(f, "East"),
Direction::North => write!(f, "North"),
Direction::West => write!(f, "West"),
Direction::South => write!(f, "South"),
}
}
}
impl Add<Pos<i32>> for Direction {
type Output = Pos<i32>;
fn add(self, rhs: Pos<i32>) -> Self::Output {
Pos::add(rhs, self)
}
}
impl Add<Pos<i32>> for &Direction {
type Output = Pos<i32>;
fn add(self, rhs: Pos<i32>) -> Self::Output {
Pos::add(rhs, *self)
}
}
impl Add<&Pos<i32>> for Direction {
type Output = Pos<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output {
Pos::add(*rhs, self)
}
}
impl Add<&Pos<i32>> for &Direction {
type Output = Pos<i32>;
fn add(self, rhs: &Pos<i32>) -> Self::Output {
Pos::add(*rhs, *self)
}
}
impl Add<Turn> for Direction {
type Output = Self;
fn add(self, rhs: Turn) -> Self {
self.turn(rhs)
}
}
impl Add<&Turn> for Direction {
type Output = Self;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(self, *rhs)
}
}
impl Add<Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: Turn) -> Direction {
Direction::add(*self, rhs)
}
}
impl Add<&Turn> for &Direction {
type Output = Direction;
fn add(self, rhs: &Turn) -> Direction {
Direction::add(*self, *rhs)
}
}

5
src/common/file.rs Normal file
View file

@ -0,0 +1,5 @@
use std::{fs, io};
pub fn read_data(day_num: usize, file: &str) -> io::Result<String> {
fs::read_to_string(format!("data/day{:02}/{}", day_num, file))
}

94
src/common/helper.rs Normal file
View file

@ -0,0 +1,94 @@
#![allow(dead_code)]
use std::fmt::Display;
pub fn join<T: Display>(lst: &[T], sep: &str) -> String {
lst.iter()
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join(sep)
}
pub fn zip2<A, B>(o1: Option<A>, o2: Option<B>) -> Option<(A, B)> {
o1.zip(o2)
}
pub fn zip3<A, B, C>(o1: Option<A>, o2: Option<B>, o3: Option<C>) -> Option<(A, B, C)> {
match (o1, o2, o3) {
(Some(a), Some(b), Some(c)) => Some((a, b, c)),
_ => None,
}
}
pub fn zip4<A, B, C, D>(
o1: Option<A>,
o2: Option<B>,
o3: Option<C>,
o4: Option<D>,
) -> Option<(A, B, C, D)> {
match (o1, o2, o3, o4) {
(Some(a), Some(b), Some(c), Some(d)) => Some((a, b, c, d)),
_ => None,
}
}
pub fn zip5<A, B, C, D, E>(
o1: Option<A>,
o2: Option<B>,
o3: Option<C>,
o4: Option<D>,
o5: Option<E>,
) -> Option<(A, B, C, D, E)> {
match (o1, o2, o3, o4, o5) {
(Some(a), Some(b), Some(c), Some(d), Some(e)) => Some((a, b, c, d, e)),
_ => None,
}
}
pub fn zip6<A, B, C, D, E, F>(
o1: Option<A>,
o2: Option<B>,
o3: Option<C>,
o4: Option<D>,
o5: Option<E>,
o6: Option<F>,
) -> Option<(A, B, C, D, E, F)> {
match (o1, o2, o3, o4, o5, o6) {
(Some(a), Some(b), Some(c), Some(d), Some(e), Some(f)) => Some((a, b, c, d, e, f)),
_ => None,
}
}
pub fn zip7<A, B, C, D, E, F, G>(
o1: Option<A>,
o2: Option<B>,
o3: Option<C>,
o4: Option<D>,
o5: Option<E>,
o6: Option<F>,
o7: Option<G>,
) -> Option<(A, B, C, D, E, F, G)> {
match (o1, o2, o3, o4, o5, o6, o7) {
(Some(a), Some(b), Some(c), Some(d), Some(e), Some(f), Some(g)) => {
Some((a, b, c, d, e, f, g))
}
_ => None,
}
}
pub fn zip8<A, B, C, D, E, F, G, H>(
o1: Option<A>,
o2: Option<B>,
o3: Option<C>,
o4: Option<D>,
o5: Option<E>,
o6: Option<F>,
o7: Option<G>,
o8: Option<H>,
) -> Option<(A, B, C, D, E, F, G, H)> {
match (o1, o2, o3, o4, o5, o6, o7, o8) {
(Some(a), Some(b), Some(c), Some(d), Some(e), Some(f), Some(g), Some(h)) => {
Some((a, b, c, d, e, f, g, h))
}
_ => None,
}
}

165
src/common/math.rs Normal file
View file

@ -0,0 +1,165 @@
#![allow(dead_code)]
use thiserror::Error;
use super::number::Number;
#[derive(Error, Debug)]
pub enum MathError {
#[error("We can not calculate so close to the ceiling")]
TooHigh,
#[error("Need positive modulo")]
NeedPositiveModulo,
#[error("Need non negative exponent")]
NeedNonNegativeExponent,
}
fn non_zero_gcd<T>(mut a: T, mut b: T) -> T
where
T: Number,
{
while b != T::ZERO {
let t = a % b;
a = b;
b = t;
}
a.abs()
}
pub fn gcd<T>(a: T, b: T) -> T
where
T: Number,
{
if a == T::ZERO {
b.abs()
} else if b == T::ZERO {
a.abs()
} else {
non_zero_gcd(a, b)
}
}
pub fn lcm<T>(a: T, b: T) -> T
where
T: Number,
{
if a == T::ZERO || b == T::ZERO {
T::ZERO
} else {
a * b / non_zero_gcd(a, b)
}
}
pub fn modulus_inv<T>(num: T, modulo: T) -> Option<T>
where
T: Number,
{
let num = num.rem_euclid(modulo);
let mut s = (T::ZERO, T::ONE);
let mut r = (modulo, num);
while r.0 != T::ZERO {
let q = r.1 / r.0;
r = (r.1 - q * r.0, r.0);
s = (s.1 - q * s.0, s.0);
}
if r.1 != T::ONE {
None
} else {
Some(s.1.rem_euclid(modulo))
}
}
pub fn modulus_mul<T>(a: T, b: T, modulo: T) -> Result<T, MathError>
where
T: Number,
{
let mul = if let Some(mul) = a.checked_mul(b) {
mul
} else if T::MAX >> T::ONE >= a {
let start = if b.is_odd() { a } else { T::ZERO };
start + modulus_mul((a << T::ONE).rem_euclid(modulo), b >> T::ONE, modulo)?
} else {
return Err(MathError::TooHigh);
};
Ok(mul.rem_euclid(modulo))
}
pub fn modulus_exp<T>(base: T, exponent: T, modulo: T) -> Result<T, MathError>
where
T: Number,
{
if modulo < T::ONE {
return Err(MathError::NeedPositiveModulo);
}
if exponent < T::ZERO {
return Err(MathError::NeedNonNegativeExponent);
}
if modulo == T::ONE {
Ok(T::ZERO)
} else {
let mut result = T::ONE;
let mut base = base.rem_euclid(modulo);
let mut exponent = exponent;
while exponent > T::ZERO {
if exponent.is_odd() {
result = modulus_mul(result, base, modulo)?;
}
exponent = exponent >> T::ONE;
base = modulus_mul(base, base, modulo)?;
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn some_simple_gcd() {
assert_eq!(5, gcd(10, 15));
assert_eq!(7, gcd(21, 49));
assert_eq!(1, gcd(13, 17));
}
#[test]
fn some_simple_lcm() {
assert_eq!(18, lcm(6, 9));
assert_eq!(20, lcm(5, 4));
}
#[test]
fn test_modulo_mul() -> Result<(), MathError> {
let a = 1_234_567_890_123_456i64;
let b = 98_765;
let result = modulus_mul(a, b, 3_333_333_333_333_333)?;
assert_eq!(result, 2_097_668_043_144_033);
Ok(())
}
#[test]
fn test_modulo_exp() -> Result<(), MathError> {
let base = 4;
let exponent = 13;
let modulo = 497;
let result = modulus_exp(base, exponent, modulo)?;
assert_eq!(result, 445);
Ok(())
}
#[test]
fn test_inverse_modulo() {
let num = 3;
let modulo = 10;
let inv = modulus_inv(num, modulo);
assert_eq!(inv, Some(7));
}
}

9
src/common/mod.rs Normal file
View file

@ -0,0 +1,9 @@
pub mod area;
pub mod direction;
pub mod file;
pub mod helper;
pub mod math;
pub mod number;
pub mod permutations;
pub mod pos;
pub mod turn;

102
src/common/number.rs Normal file
View file

@ -0,0 +1,102 @@
use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Rem, Shl, Shr, Sub, SubAssign};
pub trait Number:
Copy
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ Rem<Output = Self>
+ Shr<Output = Self>
+ Shl<Output = Self>
+ AddAssign
+ SubAssign
+ BitAnd<Output = Self>
+ PartialEq
+ PartialOrd
+ Ord
{
const ZERO: Self;
const ONE: Self;
const MAX: Self;
fn abs(self) -> Self;
fn checked_mul(self, rhs: Self) -> Option<Self>;
fn rem_euclid(self, rhs: Self) -> Self;
fn is_odd(&self) -> bool {
*self & Self::ONE == Self::ONE
}
fn is_even(&self) -> bool {
!self.is_odd()
}
fn as_f64(self) -> f64;
}
impl Number for i32 {
const ZERO: i32 = 0i32;
const ONE: i32 = 1i32;
const MAX: i32 = i32::MAX;
fn abs(self) -> Self {
(self as i32).abs()
}
fn rem_euclid(self, rhs: Self) -> Self {
(self as i32).rem_euclid(rhs)
}
fn checked_mul(self, rhs: Self) -> Option<Self> {
(self as i32).checked_mul(rhs)
}
fn as_f64(self) -> f64 {
self as f64
}
}
impl Number for i64 {
const ZERO: i64 = 0i64;
const ONE: i64 = 1i64;
const MAX: i64 = i64::MAX;
fn abs(self) -> Self {
(self as i64).abs()
}
fn rem_euclid(self, rhs: Self) -> Self {
(self as i64).rem_euclid(rhs)
}
fn checked_mul(self, rhs: Self) -> Option<Self> {
(self as i64).checked_mul(rhs)
}
fn as_f64(self) -> f64 {
self as f64
}
}
impl Number for i128 {
const ZERO: i128 = 0i128;
const ONE: i128 = 1i128;
const MAX: i128 = i128::MAX;
fn abs(self) -> Self {
(self as i128).abs()
}
fn rem_euclid(self, rhs: Self) -> Self {
(self as i128).rem_euclid(rhs)
}
fn checked_mul(self, rhs: Self) -> Option<Self> {
(self as i128).checked_mul(rhs)
}
fn as_f64(self) -> f64 {
self as f64
}
}

152
src/common/permutations.rs Normal file
View file

@ -0,0 +1,152 @@
use std::{cell::RefCell, rc::Rc};
pub trait PermutateExt<T>: IntoIterator<Item = T> {
fn permutate(&self) -> Permutations<'_, T>;
}
impl<T> PermutateExt<T> for Vec<T> {
fn permutate(&self) -> Permutations<'_, T> {
let list = self.into_iter().collect::<Vec<_>>();
Permutations {
list: Rc::new(RefCell::new(list)),
start: 0,
current: 0,
len: self.len(),
maybe_tail: None,
}
}
}
pub struct Permutations<'a, T> {
list: Rc<RefCell<Vec<&'a T>>>,
start: usize,
current: usize,
len: usize,
maybe_tail: Option<Box<Permutations<'a, T>>>,
}
impl<'a, T> Iterator for Permutations<'a, T> {
type Item = Vec<&'a T>;
fn next(&mut self) -> Option<Vec<&'a T>> {
if self.current >= self.len {
None
} else if self.start + 1 == self.len {
self.current += 1;
Some(self.list.borrow().clone())
} else {
if let Some(mut tail) = self.maybe_tail.take() {
if let Some(result) = tail.next() {
self.maybe_tail = Some(tail);
return Some(result);
} else {
let mut borrow = (*self.list).borrow_mut();
// Swapping prev first item back to its original osition
for p in self.start..self.current {
borrow.swap(p, p + 1);
}
self.current += 1;
if self.current >= self.len {
return None;
}
// Getting next first item for next iteration
for p in (self.start..self.current).rev() {
borrow.swap(p, p + 1);
}
}
}
let mut rest = Box::new(Permutations {
len: self.len,
list: self.list.clone(),
current: self.start + 1,
start: self.start + 1,
maybe_tail: None,
});
let result = rest.next();
self.maybe_tail = Some(rest);
result
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_zero() {
let input: Vec<i32> = vec![];
let result = input.permutate().collect::<Vec<_>>();
let expected: Vec<Vec<&i32>> = vec![];
assert_eq!(result, expected);
}
#[test]
fn test_one() {
let input = vec![1];
let result = input.permutate().collect::<Vec<_>>();
let expected = vec![[&1]];
assert_eq!(result, expected);
}
#[test]
fn test_two() {
let input = vec![1, 2];
let result = input.permutate().collect::<Vec<_>>();
let expected = vec![[&1, &2], [&2, &1]];
assert_eq!(result, expected);
}
#[test]
fn test_three() {
let input = vec![1, 2, 3];
let result = input.permutate().collect::<Vec<_>>();
let expected = vec![
[&1, &2, &3],
[&1, &3, &2],
[&2, &1, &3],
[&2, &3, &1],
[&3, &1, &2],
[&3, &2, &1],
];
assert_eq!(result, expected);
}
#[test]
fn test_four() {
let input = vec![1, 2, 3, 4];
let result = input.permutate().collect::<Vec<_>>();
let expected = vec![
[&1, &2, &3, &4],
[&1, &2, &4, &3],
[&1, &3, &2, &4],
[&1, &3, &4, &2],
[&1, &4, &2, &3],
[&1, &4, &3, &2],
[&2, &1, &3, &4],
[&2, &1, &4, &3],
[&2, &3, &1, &4],
[&2, &3, &4, &1],
[&2, &4, &1, &3],
[&2, &4, &3, &1],
[&3, &1, &2, &4],
[&3, &1, &4, &2],
[&3, &2, &1, &4],
[&3, &2, &4, &1],
[&3, &4, &1, &2],
[&3, &4, &2, &1],
[&4, &1, &2, &3],
[&4, &1, &3, &2],
[&4, &2, &1, &3],
[&4, &2, &3, &1],
[&4, &3, &1, &2],
[&4, &3, &2, &1],
];
assert_eq!(result, expected);
}
}

227
src/common/pos.rs Normal file
View file

@ -0,0 +1,227 @@
use super::{direction::Direction, math::gcd, number::Number};
use std::fmt;
use std::ops::{Add, Mul, Sub};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct Pos<T>(T, T)
where
T: Number;
impl<T> Pos<T>
where
T: Number,
{
pub fn new(x: T, y: T) -> Pos<T> {
Pos(x, y)
}
pub fn x(&self) -> T {
self.0
}
pub fn y(&self) -> T {
self.1
}
pub fn max_components(&self, other: &Pos<T>) -> Self {
Self(self.0.max(other.0), self.1.max(other.1))
}
pub fn min_components(&self, other: &Pos<T>) -> Self {
Self(self.0.min(other.0), self.1.min(other.1))
}
pub fn abs(&self) -> T {
self.0.abs() + self.1.abs()
}
pub fn normalize(&self) -> (Pos<T>, T) {
if self.0 == T::ZERO && self.1 == T::ZERO {
(*self, T::ONE)
} else {
let ggt = gcd(self.0, self.1);
(Pos::new(self.0 / ggt, self.1 / ggt), ggt)
}
}
pub fn angle(&self) -> f64 {
self.1.as_f64().atan2(self.0.as_f64())
}
pub fn angle2(&self) -> f64 {
(-self.0.as_f64().atan2(-self.1.as_f64()) + std::f64::consts::PI)
.rem_euclid(2.0 * std::f64::consts::PI)
}
}
impl<T> fmt::Display for Pos<T>
where
T: Number + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.0, self.1)
}
}
impl<T> Add for Pos<T>
where
T: Number,
{
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Pos(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl<T> Add for &Pos<T>
where
T: Number,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: Self) -> Self::Output {
Pos::add(*self, *rhs)
}
}
impl<T> Add<&Pos<T>> for Pos<T>
where
T: Number,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: &Self) -> Self::Output {
Pos::add(self, *rhs)
}
}
impl<T> Add<Pos<T>> for &Pos<T>
where
T: Number,
{
type Output = <Pos<T> as Add<Pos<T>>>::Output;
fn add(self, rhs: Pos<T>) -> Self::Output {
Pos::add(*self, rhs)
}
}
impl<T> Add<(T, T)> for Pos<T>
where
T: Number,
{
type Output = Self;
fn add(self, rhs: (T, T)) -> Self::Output {
Pos(self.0 + rhs.0, self.1 + rhs.1)
}
}
impl Add<Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for Pos<i32> {
type Output = Self;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(self, rhs.as_pos())
}
}
impl Add<&Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: &Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl Add<Direction> for &Pos<i32> {
type Output = Pos<i32>;
fn add(self, rhs: Direction) -> Self::Output {
Pos::add(*self, rhs.as_pos())
}
}
impl<T> Sub for Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn sub(self, rhs: Self) -> Self::Output {
Pos(self.0 - rhs.0, self.1 - rhs.1)
}
}
impl<T> Sub<&Self> for Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn sub(self, rhs: &Self) -> Self::Output {
Pos::sub(self, *rhs)
}
}
impl<T> Sub for &Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn sub(self, rhs: &Pos<T>) -> Self::Output {
Pos::sub(*self, *rhs)
}
}
impl<T> Sub<Pos<T>> for &Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn sub(self, rhs: Pos<T>) -> Self::Output {
Pos::sub(*self, rhs)
}
}
impl<T> Mul<T> for Pos<T>
where
T: Number,
{
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
Pos(self.0 * rhs, self.1 * rhs)
}
}
impl<T> Mul<T> for &Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn mul(self, rhs: T) -> Self::Output {
Pos::mul(*self, rhs)
}
}
impl<T> Mul<&T> for Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(self, *rhs)
}
}
impl<T> Mul<&T> for &Pos<T>
where
T: Number,
{
type Output = Pos<T>;
fn mul(self, rhs: &T) -> Self::Output {
Pos::mul(*self, *rhs)
}
}

121
src/common/turn.rs Normal file
View file

@ -0,0 +1,121 @@
use super::direction::Direction;
use std::{fmt::Display, ops::Add};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Turn {
Left,
Right,
Back,
Forward,
}
use Turn::*;
impl Turn {
pub fn to_left(&self) -> Turn {
match *self {
Left => Back,
Back => Right,
Right => Forward,
Forward => Left,
}
}
pub fn to_right(&self) -> Turn {
match *self {
Left => Forward,
Back => Left,
Right => Back,
Forward => Right,
}
}
pub fn to_back(&self) -> Turn {
match *self {
Left => Right,
Back => Forward,
Right => Left,
Forward => Back,
}
}
}
impl Display for Turn {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Turn::Left => write!(f, "Left"),
Turn::Right => write!(f, "Right"),
Turn::Forward => write!(f, "Forward"),
Turn::Back => write!(f, "Back"),
}
}
}
impl Add for Turn {
type Output = Turn;
fn add(self, rhs: Turn) -> Self::Output {
match rhs {
Left => self.to_left(),
Back => self.to_back(),
Right => self.to_right(),
Forward => self,
}
}
}
impl Add for &Turn {
type Output = Turn;
fn add(self, rhs: &Turn) -> Self::Output {
Turn::add(*self, *rhs)
}
}
impl Add<&Turn> for Turn {
type Output = Turn;
fn add(self, rhs: &Turn) -> Self::Output {
Turn::add(self, *rhs)
}
}
impl Add<Turn> for &Turn {
type Output = Turn;
fn add(self, rhs: Turn) -> Self::Output {
Turn::add(*self, rhs)
}
}
impl Add<Direction> for Turn {
type Output = Direction;
fn add(self, rhs: Direction) -> Self::Output {
rhs.turn(self)
}
}
impl Add<Direction> for &Turn {
type Output = Direction;
fn add(self, rhs: Direction) -> Self::Output {
rhs.turn(*self)
}
}
impl Add<&Direction> for Turn {
type Output = Direction;
fn add(self, rhs: &Direction) -> Self::Output {
rhs.turn(self)
}
}
impl Add<&Direction> for &Turn {
type Output = Direction;
fn add(self, rhs: &Direction) -> Self::Output {
rhs.turn(*self)
}
}

20
src/days/day01/mod.rs Normal file
View file

@ -0,0 +1,20 @@
use anyhow::Result;
use super::template::{DayTrait, ResultType};
pub struct Day;
const DAY_NUMBER: usize = 1;
impl DayTrait for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: String) -> Result<ResultType> {
Ok(ResultType::NoResult)
}
fn part2(&self, lines: String) -> Result<ResultType> {
Ok(ResultType::NoResult)
}
}

20
src/days/day__/mod.rs Normal file
View file

@ -0,0 +1,20 @@
use anyhow::Result;
use super::template::{Day, ResultType};
pub struct Day;
const DAY_NUMBER: usize = 0;
impl DayTemplate for Day {
fn get_day_number(&self) -> usize {
DAY_NUMBER
}
fn part1(&self, lines: String) -> Result<ResultType> {
Ok(ResultType::NoResult)
}
fn part2(&self, lines: String) -> Result<ResultType> {
Ok(ResultType::NoResult)
}
}

38
src/days/mod.rs Normal file
View file

@ -0,0 +1,38 @@
use anyhow::Result;
use self::{day01::Day, template::DayTrait};
use thiserror::Error;
pub use template::ResultType;
mod day01;
mod template;
#[derive(Debug, Error)]
pub enum TemplateError {
#[error("Not a valid day number: {0}")]
InvalidNumber(usize),
}
pub struct DayProvider {
days: Vec<Box<dyn DayTrait>>,
}
impl DayProvider {
pub fn create() -> DayProvider {
DayProvider {
days: vec![Box::new(Day)],
}
}
pub fn get_day(&self, day_num: usize) -> Result<&Box<dyn DayTrait>> {
Ok(self
.days
.get(day_num - 1)
.ok_or(TemplateError::InvalidNumber(day_num))?)
}
pub fn get_all_days(&self) -> &[Box<dyn DayTrait>] {
&self.days
}
}

14
src/days/template.rs Normal file
View file

@ -0,0 +1,14 @@
use anyhow::Result;
pub enum ResultType {
IntResult(i64),
StringResult(String),
LinesResult(String),
NoResult,
}
pub trait DayTrait {
fn get_day_number(&self) -> usize;
fn part1(&self, lines: String) -> Result<ResultType>;
fn part2(&self, lines: String) -> Result<ResultType>;
}

26
src/macros.rs Normal file
View file

@ -0,0 +1,26 @@
// https://stackoverflow.com/a/28392068
#[macro_export]
macro_rules! hashmap {
() => {
::std::collections::HashMap::new()
};
($( $key: expr => $val: expr ),+ $(,)?) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )+
map
}}
}
#[macro_export]
macro_rules! hashset {
() => {
::std::collections::HashSet::new()
};
($( $key: expr ),+ $(,)?) => {{
let mut set = ::std::collections::HashSet::new();
$( set.insert($key); )+
set
}}
}

27
src/main.rs Normal file
View file

@ -0,0 +1,27 @@
mod common;
mod days;
mod macros;
use std::error::Error;
use common::file::read_data;
use days::{DayProvider, ResultType};
fn main() -> Result<(), Box<dyn Error>> {
let day_provider = DayProvider::create();
//let params = env::args().skip(1).collect::<Vec<_>>();
let day = day_provider.get_day(1)?;
let lines = read_data(day.get_day_number(), "input.txt")?;
match day.part1(lines)? {
ResultType::IntResult(value) => {
println!("Day {:02} part {}: {}", day.get_day_number(), 1, value);
}
ResultType::StringResult(_) => todo!(),
ResultType::LinesResult(_) => todo!(),
ResultType::NoResult => todo!(),
}
Ok(())
}