commit 284f099d3e49cda881fe0d2eeae8a11c66de6808 Author: Ruediger Ludwig Date: Tue Jan 24 10:13:28 2023 +0100 Initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8b12be --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d1f66fe --- /dev/null +++ b/.vscode/launch.json @@ -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?" + } + ] +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c2f6fa9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "advent2022" +version = "1.0.0" +authors = ["Ruediger Ludwig "] +edition = "2021" + +[dependencies] +anyhow = "1.0.68" +thiserror = "1.0.38" diff --git a/README.md b/README.md new file mode 100644 index 0000000..aaddfc4 --- /dev/null +++ b/README.md @@ -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/) diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/UNLICENSE @@ -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 diff --git a/data/day01/input.txt b/data/day01/input.txt new file mode 100644 index 0000000..5213866 --- /dev/null +++ b/data/day01/input.txt @@ -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 diff --git a/src/common/area.rs b/src/common/area.rs new file mode 100644 index 0000000..e1966a7 --- /dev/null +++ b/src/common/area.rs @@ -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 +where + T: Number, +{ + lower_left: Pos, + upper_right: Pos, +} + +impl Area +where + T: Number + Ord, +{ + pub fn new(p1: Pos, p2: Pos) -> Area { + Area { + lower_left: p1.min_components(&p2), + upper_right: p1.max_components(&p2), + } + } + + pub fn extend(&self, pos: Pos) -> Area { + 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 { + self.lower_left + } + + pub fn get_upper_right(&self) -> Pos { + self.upper_right + } + + pub fn contains(&self, pos: Pos) -> 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 +where + T: Number + Ord + 'a, +{ + pub fn from_iterator(mut iter: I) -> Option + where + I: Iterator>, + { + 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 Area +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 Area +where + T: Number, +{ + #[allow(dead_code)] + pub fn area(&self) -> T { + self.width() * self.height() + } +} + +impl Display for Area +where + T: Number + Display, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}-{}]", self.lower_left, self.upper_right) + } +} + +impl Area +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, + row: T, + ascending: bool, +} + +impl<'a, T> RowIterator<'a, T> +where + T: Number, +{ + fn new(area: &'a Area, 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 { + 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, + 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, + row: T, + col: T, + ascending: bool, +} + +impl<'a, T> Iterator for ColIterator<'a, T> +where + T: Number, +{ + type Item = Pos; + fn next(&mut self) -> Option { + 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, + row: T, + col: T, + ascending: bool, +} + +impl<'a, T> CellIterator<'a, T> +where + T: Number, +{ + pub fn new(area: &'a Area, 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; + + fn next(&mut self) -> Option { + 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::>(); + 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); + } +} diff --git a/src/common/direction.rs b/src/common/direction.rs new file mode 100644 index 0000000..cd14a27 --- /dev/null +++ b/src/common/direction.rs @@ -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 { + 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> for Direction { + type Output = Pos; + + fn add(self, rhs: Pos) -> Self::Output { + Pos::add(rhs, self) + } +} + +impl Add> for &Direction { + type Output = Pos; + + fn add(self, rhs: Pos) -> Self::Output { + Pos::add(rhs, *self) + } +} + +impl Add<&Pos> for Direction { + type Output = Pos; + + fn add(self, rhs: &Pos) -> Self::Output { + Pos::add(*rhs, self) + } +} + +impl Add<&Pos> for &Direction { + type Output = Pos; + + fn add(self, rhs: &Pos) -> Self::Output { + Pos::add(*rhs, *self) + } +} + +impl Add 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 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) + } +} diff --git a/src/common/file.rs b/src/common/file.rs new file mode 100644 index 0000000..e825e48 --- /dev/null +++ b/src/common/file.rs @@ -0,0 +1,5 @@ +use std::{fs, io}; + +pub fn read_data(day_num: usize, file: &str) -> io::Result { + fs::read_to_string(format!("data/day{:02}/{}", day_num, file)) +} diff --git a/src/common/helper.rs b/src/common/helper.rs new file mode 100644 index 0000000..71d6bd0 --- /dev/null +++ b/src/common/helper.rs @@ -0,0 +1,94 @@ +#![allow(dead_code)] +use std::fmt::Display; + +pub fn join(lst: &[T], sep: &str) -> String { + lst.iter() + .map(|item| item.to_string()) + .collect::>() + .join(sep) +} + +pub fn zip2(o1: Option, o2: Option) -> Option<(A, B)> { + o1.zip(o2) +} + +pub fn zip3(o1: Option, o2: Option, o3: Option) -> Option<(A, B, C)> { + match (o1, o2, o3) { + (Some(a), Some(b), Some(c)) => Some((a, b, c)), + _ => None, + } +} + +pub fn zip4( + o1: Option, + o2: Option, + o3: Option, + o4: Option, +) -> 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( + o1: Option, + o2: Option, + o3: Option, + o4: Option, + o5: Option, +) -> 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( + o1: Option, + o2: Option, + o3: Option, + o4: Option, + o5: Option, + o6: Option, +) -> 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( + o1: Option, + o2: Option, + o3: Option, + o4: Option, + o5: Option, + o6: Option, + o7: Option, +) -> 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( + o1: Option, + o2: Option, + o3: Option, + o4: Option, + o5: Option, + o6: Option, + o7: Option, + o8: Option, +) -> 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, + } +} diff --git a/src/common/math.rs b/src/common/math.rs new file mode 100644 index 0000000..a2ce540 --- /dev/null +++ b/src/common/math.rs @@ -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(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(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(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(num: T, modulo: T) -> Option +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(a: T, b: T, modulo: T) -> Result +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(base: T, exponent: T, modulo: T) -> Result +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)); + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs new file mode 100644 index 0000000..e615dc0 --- /dev/null +++ b/src/common/mod.rs @@ -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; diff --git a/src/common/number.rs b/src/common/number.rs new file mode 100644 index 0000000..f562b6f --- /dev/null +++ b/src/common/number.rs @@ -0,0 +1,102 @@ +use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Rem, Shl, Shr, Sub, SubAssign}; + +pub trait Number: + Copy + + Add + + Sub + + Mul + + Div + + Rem + + Shr + + Shl + + AddAssign + + SubAssign + + BitAnd + + PartialEq + + PartialOrd + + Ord +{ + const ZERO: Self; + const ONE: Self; + const MAX: Self; + + fn abs(self) -> Self; + fn checked_mul(self, rhs: Self) -> Option; + 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 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 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 as i128).checked_mul(rhs) + } + + fn as_f64(self) -> f64 { + self as f64 + } +} diff --git a/src/common/permutations.rs b/src/common/permutations.rs new file mode 100644 index 0000000..34d6fdd --- /dev/null +++ b/src/common/permutations.rs @@ -0,0 +1,152 @@ +use std::{cell::RefCell, rc::Rc}; + +pub trait PermutateExt: IntoIterator { + fn permutate(&self) -> Permutations<'_, T>; +} + +impl PermutateExt for Vec { + fn permutate(&self) -> Permutations<'_, T> { + let list = self.into_iter().collect::>(); + Permutations { + list: Rc::new(RefCell::new(list)), + + start: 0, + current: 0, + len: self.len(), + maybe_tail: None, + } + } +} + +pub struct Permutations<'a, T> { + list: Rc>>, + start: usize, + current: usize, + len: usize, + maybe_tail: Option>>, +} + +impl<'a, T> Iterator for Permutations<'a, T> { + type Item = Vec<&'a T>; + + fn next(&mut self) -> Option> { + 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 = vec![]; + let result = input.permutate().collect::>(); + let expected: Vec> = vec![]; + assert_eq!(result, expected); + } + + #[test] + fn test_one() { + let input = vec![1]; + let result = input.permutate().collect::>(); + let expected = vec![[&1]]; + assert_eq!(result, expected); + } + + #[test] + fn test_two() { + let input = vec![1, 2]; + let result = input.permutate().collect::>(); + 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::>(); + 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::>(); + 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); + } +} diff --git a/src/common/pos.rs b/src/common/pos.rs new file mode 100644 index 0000000..99f3f89 --- /dev/null +++ b/src/common/pos.rs @@ -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) +where + T: Number; + +impl Pos +where + T: Number, +{ + pub fn new(x: T, y: T) -> Pos { + Pos(x, y) + } + + pub fn x(&self) -> T { + self.0 + } + + pub fn y(&self) -> T { + self.1 + } + + pub fn max_components(&self, other: &Pos) -> Self { + Self(self.0.max(other.0), self.1.max(other.1)) + } + + pub fn min_components(&self, other: &Pos) -> 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) { + 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 fmt::Display for Pos +where + T: Number + fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({}, {})", self.0, self.1) + } +} + +impl Add for Pos +where + T: Number, +{ + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + Pos(self.0 + rhs.0, self.1 + rhs.1) + } +} + +impl Add for &Pos +where + T: Number, +{ + type Output = as Add>>::Output; + + fn add(self, rhs: Self) -> Self::Output { + Pos::add(*self, *rhs) + } +} + +impl Add<&Pos> for Pos +where + T: Number, +{ + type Output = as Add>>::Output; + fn add(self, rhs: &Self) -> Self::Output { + Pos::add(self, *rhs) + } +} + +impl Add> for &Pos +where + T: Number, +{ + type Output = as Add>>::Output; + fn add(self, rhs: Pos) -> Self::Output { + Pos::add(*self, rhs) + } +} + +impl Add<(T, T)> for Pos +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 for Pos { + type Output = Self; + + fn add(self, rhs: Direction) -> Self::Output { + Pos::add(self, rhs.as_pos()) + } +} + +impl Add<&Direction> for Pos { + type Output = Self; + + fn add(self, rhs: &Direction) -> Self::Output { + Pos::add(self, rhs.as_pos()) + } +} + +impl Add<&Direction> for &Pos { + type Output = Pos; + + fn add(self, rhs: &Direction) -> Self::Output { + Pos::add(*self, rhs.as_pos()) + } +} + +impl Add for &Pos { + type Output = Pos; + + fn add(self, rhs: Direction) -> Self::Output { + Pos::add(*self, rhs.as_pos()) + } +} + +impl Sub for Pos +where + T: Number, +{ + type Output = Pos; + fn sub(self, rhs: Self) -> Self::Output { + Pos(self.0 - rhs.0, self.1 - rhs.1) + } +} + +impl Sub<&Self> for Pos +where + T: Number, +{ + type Output = Pos; + fn sub(self, rhs: &Self) -> Self::Output { + Pos::sub(self, *rhs) + } +} + +impl Sub for &Pos +where + T: Number, +{ + type Output = Pos; + fn sub(self, rhs: &Pos) -> Self::Output { + Pos::sub(*self, *rhs) + } +} + +impl Sub> for &Pos +where + T: Number, +{ + type Output = Pos; + fn sub(self, rhs: Pos) -> Self::Output { + Pos::sub(*self, rhs) + } +} + +impl Mul for Pos +where + T: Number, +{ + type Output = Self; + fn mul(self, rhs: T) -> Self::Output { + Pos(self.0 * rhs, self.1 * rhs) + } +} + +impl Mul for &Pos +where + T: Number, +{ + type Output = Pos; + fn mul(self, rhs: T) -> Self::Output { + Pos::mul(*self, rhs) + } +} + +impl Mul<&T> for Pos +where + T: Number, +{ + type Output = Pos; + fn mul(self, rhs: &T) -> Self::Output { + Pos::mul(self, *rhs) + } +} + +impl Mul<&T> for &Pos +where + T: Number, +{ + type Output = Pos; + fn mul(self, rhs: &T) -> Self::Output { + Pos::mul(*self, *rhs) + } +} diff --git a/src/common/turn.rs b/src/common/turn.rs new file mode 100644 index 0000000..d810032 --- /dev/null +++ b/src/common/turn.rs @@ -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 for &Turn { + type Output = Turn; + + fn add(self, rhs: Turn) -> Self::Output { + Turn::add(*self, rhs) + } +} + +impl Add for Turn { + type Output = Direction; + + fn add(self, rhs: Direction) -> Self::Output { + rhs.turn(self) + } +} + +impl Add 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) + } +} diff --git a/src/days/day01/mod.rs b/src/days/day01/mod.rs new file mode 100644 index 0000000..e33c5e2 --- /dev/null +++ b/src/days/day01/mod.rs @@ -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 { + Ok(ResultType::NoResult) + } + + fn part2(&self, lines: String) -> Result { + Ok(ResultType::NoResult) + } +} diff --git a/src/days/day__/mod.rs b/src/days/day__/mod.rs new file mode 100644 index 0000000..a1dbcae --- /dev/null +++ b/src/days/day__/mod.rs @@ -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 { + Ok(ResultType::NoResult) + } + + fn part2(&self, lines: String) -> Result { + Ok(ResultType::NoResult) + } +} diff --git a/src/days/mod.rs b/src/days/mod.rs new file mode 100644 index 0000000..025f731 --- /dev/null +++ b/src/days/mod.rs @@ -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>, +} + +impl DayProvider { + pub fn create() -> DayProvider { + DayProvider { + days: vec![Box::new(Day)], + } + } + + pub fn get_day(&self, day_num: usize) -> Result<&Box> { + Ok(self + .days + .get(day_num - 1) + .ok_or(TemplateError::InvalidNumber(day_num))?) + } + + pub fn get_all_days(&self) -> &[Box] { + &self.days + } +} diff --git a/src/days/template.rs b/src/days/template.rs new file mode 100644 index 0000000..e1cef4b --- /dev/null +++ b/src/days/template.rs @@ -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; + fn part2(&self, lines: String) -> Result; +} diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..121fc2a --- /dev/null +++ b/src/macros.rs @@ -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 + }} +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..2a192a6 --- /dev/null +++ b/src/main.rs @@ -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> { + let day_provider = DayProvider::create(); + + //let params = env::args().skip(1).collect::>(); + + 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(()) +}