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

22
.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
# Created by https://www.toptal.com/developers/gitignore/api/rust,vscode
# Edit at https://www.toptal.com/developers/gitignore?templates=rust,vscode
### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
### vscode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# End of https://www.toptal.com/developers/gitignore/api/rust,vscode

61
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,61 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'advent2019'",
"cargo": {
"args": [
"build",
"--bin=advent2019",
"--package=advent2019"
],
"filter": {
"name": "advent2019",
"kind": "bin"
}
},
"args": [
"${input:program}"
],
"cwd": "${workspaceFolder}",
"sourceLanguages": [
"rust"
]
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'advent2019'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=advent2019",
"--package=advent2019"
],
"filter": {
"name": "advent2019",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}",
"sourceLanguages": [
"rust"
]
}
],
"inputs": [
{
"id": "program",
"type": "promptString",
"default": "day",
"description": "Which day shall I run?"
}
]
}

9
Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "advent2022"
version = "1.0.0"
authors = ["Ruediger Ludwig <github@savinien.de>"]
edition = "2021"
[dependencies]
anyhow = "1.0.68"
thiserror = "1.0.38"

13
README.md Normal file
View file

@ -0,0 +1,13 @@
# Advent of Code 2019
These are my solutions for [Advent of Code 2019](https://adventofcode.com/). Thanks to [Eric Wastl](http://was.tl) for the great puzzles and a great time I had solving them.
These are my attempts for the year 2019. I wanted to learn Rust (started at version 1.48 finished at 1.49) and I would say I succeeded to get the hange of this strange language for just a bit.
My goal was to finish at least one puzzle on a given day. And to try to use a few additional crates as possible. I did not quite succeed in the former, but at least I solved every single puzzle alone. I am quite pround of a few of them. I learned a lot. Not only about rust, but also on how to approach certain riddles.
If you look at the code and see ways I could improve it, please do not hesitate to contact me. I am always grateful for everything that makes me a better programmer.
Also, if you ever look for a programmer and think my code is any good, please also contact me at once.
All code is published under the [Unlicense](https://unlicense.org/)

24
UNLICENSE Normal file
View file

@ -0,0 +1,24 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>

100
data/day01/input.txt Normal file
View file

@ -0,0 +1,100 @@
128270
147113
61335
78766
119452
116991
70640
145446
117606
135046
70489
131072
67955
66424
126450
101418
90225
66004
136510
61695
143880
53648
58699
119214
83838
95895
66388
66755
120223
79310
93828
136686
108958
140752
85343
103800
126602
147726
88228
83380
77877
61922
75448
67095
60888
136692
63271
113742
68854
86904
110243
104642
141854
71205
76729
138540
134142
62517
63306
71363
126146
74749
76716
59135
62449
110575
134030
84072
122698
96891
69976
94501
149180
57944
64873
68192
138238
119185
137570
79274
111040
142586
120872
63586
78628
122704
147951
102593
105562
55180
64450
87466
112522
60000
149885
52154
80633
61867
86380
136024

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(())
}