From e1f6a8c01ce9edcdad6ac225bfbf5b25c6ec46ef Mon Sep 17 00:00:00 2001 From: Charles Lechasseur Date: Sat, 23 Nov 2024 02:08:04 -0500 Subject: [PATCH] chore: add helpers, clean up, prepare --- .github/workflows/ci.yml | 2 +- Cargo.lock | 213 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 6 +- README.md | 2 +- src/day_01.rs | 3 + src/helpers.rs | 6 +- src/helpers/direction.rs | 55 ++++++++++ src/helpers/looping.rs | 199 ++++++++++++++++++++++++++++++++++++ src/helpers/pt.rs | 138 +++++++++++++++++++++++++ src/helpers/pt_3d.rs | 145 ++++++++++++++++++++++++++ src/helpers/regex.rs | 20 ++++ src/input.rs | 2 +- src/input/day_01.rs | 1 + src/lib.rs | 5 + tests/days/day_01.rs | 6 ++ tests/days/mod.rs | 2 +- 16 files changed, 799 insertions(+), 6 deletions(-) create mode 100644 src/day_01.rs create mode 100644 src/helpers/direction.rs create mode 100644 src/helpers/looping.rs create mode 100644 src/helpers/pt.rs create mode 100644 src/helpers/pt_3d.rs create mode 100644 src/helpers/regex.rs create mode 100644 src/input/day_01.rs create mode 100644 tests/days/day_01.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53bbe37..2422668 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - toolchain: [ 1.56.1, stable ] + toolchain: [ 1.70.0, stable ] os: [ ubuntu ] ignore-lock: [ false ] all-features: [ true ] diff --git a/Cargo.lock b/Cargo.lock index a9ced3e..f15aeb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,3 +5,216 @@ version = 3 [[package]] name = "adventofcode2024-clp" version = "0.1.0" +dependencies = [ + "itertools", + "num", + "regex", + "strum", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" diff --git a/Cargo.toml b/Cargo.toml index 110bab1..c6039df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,10 +2,14 @@ name = "adventofcode2024-clp" version = "0.1.0" edition = "2021" -rust-version = "1.56.1" +rust-version = "1.70.0" [features] slow = [] utils = [] [dependencies] +itertools = "0.13.0" +num = "0.4.3" +regex = "1.11.1" +strum = { version = "0.26.3", features = ["derive"] } diff --git a/README.md b/README.md index 9ef791b..408a7bf 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ My solutions to the Advent of Code 2024 puzzles in Rust 🦀 ## Requirements -* [Rust](https://www.rust-lang.org/) 1.56.1 or later +* [Rust](https://www.rust-lang.org/) 1.70.0 or later ## Running the tests diff --git a/src/day_01.rs b/src/day_01.rs new file mode 100644 index 0000000..65931c0 --- /dev/null +++ b/src/day_01.rs @@ -0,0 +1,3 @@ +pub fn part_1() -> usize { + 0 +} diff --git a/src/helpers.rs b/src/helpers.rs index 8b13789..d02c5b3 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1 +1,5 @@ - +pub mod direction; +pub mod looping; +pub mod pt; +pub mod pt_3d; +pub mod regex; diff --git a/src/helpers/direction.rs b/src/helpers/direction.rs new file mode 100644 index 0000000..c0b7295 --- /dev/null +++ b/src/helpers/direction.rs @@ -0,0 +1,55 @@ +use std::ops::Neg; + +use num::{one, zero, One, Zero}; +use strum::{Display, EnumCount, FromRepr}; + +use crate::helpers::pt::Pt; + +/// ↓ ↑ ← → +#[repr(u8)] +#[derive( + Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, FromRepr, EnumCount, Display, +)] +pub enum Direction { + Right, + Down, + Left, + Up, +} + +impl Direction { + /// Turns 90 degrees to the left. + pub fn turn_left(&self) -> Self { + Self::from_repr(((*self as u8) + 3) % (Self::COUNT as u8)).unwrap() + } + + /// Turns 90 degrees to the right. + pub fn turn_right(&self) -> Self { + Self::from_repr(((*self as u8) + 1) % (Self::COUNT as u8)).unwrap() + } + + /// Turns around (e.g. performs a 180 degrees turn). + pub fn turn_around(&self) -> Self { + Self::from_repr(((*self as u8) + 2) % (Self::COUNT as u8)).unwrap() + } + + /// Returns the displacement to apply to move one step in this direction. + /// The displacement is returned as a [`Pt`]. + /// + /// # Notes + /// + /// Because this enum is meant to be used to move around a map represented as a series of rows + /// like on a computer screen, `Up`'s displacement will _subtract_ one from the Y axis, while + /// `Down`'s will _add_ one to the Y axis. + pub fn displacement(&self) -> Pt + where + T: Zero + One + Neg, + { + match self { + Direction::Right => Pt::new(one(), zero()), + Direction::Down => Pt::new(zero(), one()), + Direction::Left => Pt::new(-one::(), zero()), + Direction::Up => Pt::new(zero(), -one::()), + } + } +} diff --git a/src/helpers/looping.rs b/src/helpers/looping.rs new file mode 100644 index 0000000..b6522a9 --- /dev/null +++ b/src/helpers/looping.rs @@ -0,0 +1,199 @@ +use std::cmp::min; +use std::iter::FusedIterator; +use std::vec; + +use itertools::Itertools; + +pub trait LoopingItertools: Iterator { + fn looping(self, size: usize) -> Looping + where + Self: Sized, + Self::Item: PartialEq, + { + let mut prefix = Vec::new(); + + for e in self { + match prefix.iter().find_position(|&ve| *ve == e) { + Some((start, _)) => { + let cycle = prefix.split_off(start); + return Looping::new(prefix, cycle, size); + }, + None => prefix.push(e), + } + } + + panic!("no loop detected"); + } +} + +impl LoopingItertools for I where I: Iterator + ?Sized {} + +#[derive(Debug, Clone)] +pub struct Looping { + prefix: vec::IntoIter, + cycle: Vec, + cycle_pos: usize, + cycle_size: usize, +} + +impl Looping { + pub fn new(prefix: Vec, cycle: Vec, size: usize) -> Self { + let prefix_len = prefix.len(); + Self { + prefix: prefix.into_iter(), + cycle, + cycle_pos: 0, + cycle_size: size.saturating_sub(prefix_len), + } + } + + fn cycle_len(&self) -> usize { + self.cycle_size - self.cycle_pos + } +} + +impl Iterator for Looping +where + T: Clone, +{ + type Item = T; + + fn next(&mut self) -> Option { + match self.prefix.next() { + Some(e) => Some(e), + None if self.cycle_len() == 0 => None, + None => { + let e = self.cycle[self.cycle_pos % self.cycle.len()].clone(); + self.cycle_pos += 1; + Some(e) + }, + } + } + + fn size_hint(&self) -> (usize, Option) { + let exact = self.prefix.len() + self.cycle_len(); + (exact, Some(exact)) + } + + fn count(self) -> usize + where + Self: Sized, + { + self.len() + } + + fn last(self) -> Option + where + Self: Sized, + { + if self.len() == 0 { + return None; + } + + let last_pos_in_cycle = (self.cycle_size - 1) % self.cycle.len(); + self.cycle + .into_iter() + .nth(last_pos_in_cycle) + .or_else(|| self.prefix.last()) + } + + fn nth(&mut self, n: usize) -> Option { + let prefix_len = self.prefix.len(); + self.prefix.nth(n).or_else(|| { + self.cycle_pos = min(self.cycle_pos + (n - prefix_len), self.cycle_size); + (self.cycle_len() != 0) + .then(|| self.next()) + .unwrap_or_default() + }) + } +} + +impl ExactSizeIterator for Looping where T: Clone {} +impl FusedIterator for Looping where T: Clone {} + +#[cfg(test)] +#[cfg(feature = "utils")] +mod tests { + use super::*; + + const DATA: &[usize] = &[1, 2, 3, 4, 5, 6, 3]; + + #[test] + fn test_iterator() { + let v = DATA.iter().looping(11).copied().collect_vec(); + assert_eq!([1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3], *v.as_slice()); + } + + #[test] + fn test_exact_size() { + let mut i = DATA.iter().looping(11); + assert_eq!(11, i.len()); + assert_eq!((11, Some(11)), i.size_hint()); + + let _ = i.next(); + assert_eq!(10, i.len()); + assert_eq!((10, Some(10)), i.size_hint()); + + let _ = i.next(); + let _ = i.next(); + assert_eq!(8, i.len()); + assert_eq!((8, Some(8)), i.size_hint()); + + while i.next().is_some() {} + assert_eq!(0, i.len()); + assert_eq!((0, Some(0)), i.size_hint()); + } + + #[test] + fn test_count() { + let i = DATA.iter().looping(11); + assert_eq!(11, i.count()); + + let mut i = DATA.iter().looping(11); + let _ = i.next(); + let _ = i.next(); + let _ = i.next(); + assert_eq!(8, i.count()); + } + + #[test] + fn test_last() { + let i = DATA.iter().copied().looping(11); + assert_eq!(Some(3), i.last()); + + let mut i = DATA.iter().copied().looping(11); + let _ = i.next(); + let _ = i.next(); + let _ = i.next(); + assert_eq!(Some(3), i.last()); + + let mut i = DATA.iter().copied().looping(11); + while i.next().is_some() {} + assert_eq!(None, i.last()); + } + + #[test] + #[allow(clippy::iter_nth_zero)] + fn test_nth() { + let expected = [1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3]; + + let mut i = DATA.iter().copied().looping(11); + let mut ei = expected.iter().copied(); + while let Some(e) = i.nth(0) { + println!("{}", e); + assert_eq!(ei.next(), Some(e)); + } + assert!(ei.next().is_none()); + + let mut i = DATA.iter().copied().looping(11); + assert_eq!(Some(2), i.nth(1)); + assert_eq!(Some(4), i.nth(1)); + assert_eq!(Some(3), i.nth(2)); + assert!(i.nth(7).is_none()); + assert!(i.next().is_none()); + + let mut i = DATA.iter().copied().looping(11); + assert_eq!(Some(3), i.nth(10)); + assert!(i.next().is_none()); + } +} diff --git a/src/helpers/pt.rs b/src/helpers/pt.rs new file mode 100644 index 0000000..f417a8d --- /dev/null +++ b/src/helpers/pt.rs @@ -0,0 +1,138 @@ +use std::fmt::{Display, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; +use std::str::FromStr; +use std::sync::OnceLock; + +use num::{zero, Signed, Zero}; +use regex::Regex; + +use crate::helpers::regex::CapturesHelper; + +/// A point in 2D space. +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Pt { + pub x: T, + pub y: T, +} + +impl Pt { + pub const fn new(x: T, y: T) -> Self { + Self { x, y } + } +} + +impl From<(U, V)> for Pt +where + U: Into, + V: Into, +{ + /// Converts from a 2-number tuple to a [`Pt`]. + fn from(value: (U, V)) -> Self { + Self::new(value.0.into(), value.1.into()) + } +} + +impl From> for (U, V) +where + T: Into + Into, +{ + /// Converts from a [`Pt`] to a 2-number tuple. + fn from(value: Pt) -> Self { + (value.x.into(), value.y.into()) + } +} + +impl FromStr for Pt +where + T: FromStr, +{ + type Err = (); + + /// Parses a [`Pt`] from a string in the form `(x, y)`. + /// Parentheses and whitespace are optional. + fn from_str(s: &str) -> Result { + static REGEX: OnceLock = OnceLock::new(); + let re = REGEX.get_or_init(|| { + Regex::new(r"\(?(?-?\d+(?:\.\d*)?),\s*(?-?\d+(?:\.\d*)?)\)?$").unwrap() + }); + + let captures = re + .captures(s) + .unwrap_or_else(|| panic!("invalid Pt value: {s}")); + Ok(Self::new(captures.ez_get("x"), captures.ez_get("y"))) + } +} + +impl Display for Pt +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {})", self.x, self.y) + } +} + +impl Add for Pt +where + T: Add, +{ + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::new(self.x + rhs.x, self.y + rhs.y) + } +} + +impl AddAssign for Pt +where + T: AddAssign, +{ + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + } +} + +impl Sub for Pt +where + T: Sub, +{ + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self::new(self.x - rhs.x, self.y - rhs.y) + } +} + +impl SubAssign for Pt +where + T: SubAssign, +{ + fn sub_assign(&mut self, rhs: Self) { + self.x -= rhs.x; + self.y -= rhs.y; + } +} + +impl Zero for Pt +where + T: Zero, +{ + fn zero() -> Self { + Self::new(zero(), zero()) + } + + fn is_zero(&self) -> bool { + self.x.is_zero() && self.y.is_zero() + } +} + +/// Returns the [Manhattan distance] between two points in 2D space. +/// +/// [Manhattan distance]: https://en.wikipedia.org/wiki/Taxicab_geometry +pub fn manhattan(a: Pt, b: Pt) -> T +where + T: Signed, +{ + (a.x - b.x).abs() + (a.y - b.y).abs() +} diff --git a/src/helpers/pt_3d.rs b/src/helpers/pt_3d.rs new file mode 100644 index 0000000..17266c1 --- /dev/null +++ b/src/helpers/pt_3d.rs @@ -0,0 +1,145 @@ +use std::fmt::{Display, Formatter}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; +use std::str::FromStr; +use std::sync::OnceLock; + +use num::{zero, Signed, Zero}; +use regex::Regex; + +use crate::helpers::regex::CapturesHelper; + +/// A point in 3D space. +#[derive(Debug, Default, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Pt3d { + pub x: T, + pub y: T, + pub z: T, +} + +impl Pt3d { + pub const fn new(x: T, y: T, z: T) -> Self { + Self { x, y, z } + } +} + +impl From<(U, V, W)> for Pt3d +where + U: Into, + V: Into, + W: Into, +{ + /// Converts form a 3-number tuple to a [`Pt3D`]. + fn from(value: (U, V, W)) -> Self { + Self::new(value.0.into(), value.1.into(), value.2.into()) + } +} + +impl From> for (U, V, W) +where + T: Into + Into + Into, +{ + /// Converts from a [`Pt3D`] to a 3-number tuple. + fn from(value: Pt3d) -> Self { + (value.x.into(), value.y.into(), value.z.into()) + } +} + +impl FromStr for Pt3d +where + T: FromStr, +{ + type Err = (); + + /// Parses a [`Pt3D`] from a string in the form `(x, y, z)`. + /// Parentheses and whitespace are optional. + fn from_str(s: &str) -> Result { + static REGEX: OnceLock = OnceLock::new(); + let re = REGEX.get_or_init(|| { + Regex::new( + r"\(?(?-?\d+(?:\.\d*)?),\s*(?-?\d+(?:\.\d*)?),\s*(?-?\d+(?:\.\d*)?)\)?$", + ) + .unwrap() + }); + + let captures = re + .captures(s) + .unwrap_or_else(|| panic!("invalid Pt3d value: {s}")); + Ok(Self::new(captures.ez_get("x"), captures.ez_get("y"), captures.ez_get("z"))) + } +} + +impl Display for Pt3d +where + T: Display, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "({}, {}, {})", self.x, self.y, self.z) + } +} + +impl Add for Pt3d +where + T: Add, +{ + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + } +} + +impl AddAssign for Pt3d +where + T: AddAssign, +{ + fn add_assign(&mut self, rhs: Self) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl Sub for Pt3d +where + T: Sub, +{ + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + } +} + +impl SubAssign for Pt3d +where + T: SubAssign, +{ + fn sub_assign(&mut self, rhs: Self) { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; + } +} + +impl Zero for Pt3d +where + T: Zero, +{ + fn zero() -> Self { + Self::new(zero(), zero(), zero()) + } + + fn is_zero(&self) -> bool { + self.x.is_zero() && self.y.is_zero() && self.z.is_zero() + } +} + +/// Returns the [Manhattan distance] between two points in 3D space. +/// +/// [Manhattan distance]: https://en.wikipedia.org/wiki/Taxicab_geometry +pub fn manhattan(a: Pt3d, b: Pt3d) -> T +where + T: Signed, +{ + (a.x - b.x).abs() + (a.y - b.y).abs() + (a.z - b.z).abs() +} diff --git a/src/helpers/regex.rs b/src/helpers/regex.rs new file mode 100644 index 0000000..163e600 --- /dev/null +++ b/src/helpers/regex.rs @@ -0,0 +1,20 @@ +use std::str::FromStr; + +use regex::Captures; + +pub trait CapturesHelper { + fn ez_get(&self, name: &str) -> T + where + T: FromStr; +} + +impl<'h> CapturesHelper for Captures<'h> { + fn ez_get(&self, name: &str) -> T + where + T: FromStr, + { + self[name] + .parse::() + .unwrap_or_else(|_| panic!("invalid value for {name}: {}", &self[name])) + } +} diff --git a/src/input.rs b/src/input.rs index 8b13789..83a7f75 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1 +1 @@ - +pub mod day_01; diff --git a/src/input/day_01.rs b/src/input/day_01.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/input/day_01.rs @@ -0,0 +1 @@ + diff --git a/src/lib.rs b/src/lib.rs index 34f5bcd..d5f9daa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,7 @@ +//! Solutions to the Advent of Code 2024 puzzles in Rust 🦀 + +#![allow(dead_code)] + +pub mod day_01; pub(crate) mod helpers; pub(crate) mod input; diff --git a/tests/days/day_01.rs b/tests/days/day_01.rs new file mode 100644 index 0000000..3da5f63 --- /dev/null +++ b/tests/days/day_01.rs @@ -0,0 +1,6 @@ +use adventofcode2024_clp::day_01::part_1; + +#[test] +fn day_01_part_1() { + assert_eq!(0, part_1()); +} diff --git a/tests/days/mod.rs b/tests/days/mod.rs index 8b13789..c7c6755 100644 --- a/tests/days/mod.rs +++ b/tests/days/mod.rs @@ -1 +1 @@ - +mod day_01;