From 39071cffe432a4acfcd9bdef3b44f984ed01639d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Sun, 8 Dec 2024 18:16:12 +0100 Subject: [PATCH] feat: add day 7 --- Cargo.lock | 16 +++++ Cargo.toml | 1 + README.md | 15 +++-- data/examples/07.txt | 9 +++ src/bin/07.rs | 148 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 data/examples/07.txt create mode 100644 src/bin/07.rs diff --git a/Cargo.lock b/Cargo.lock index 3fe2fa0..e40da41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ dependencies = [ "clippy", "dhat", "fmt", + "itertools", "pico-args", "regex", "tinyjson", @@ -205,6 +206,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "fmt" version = "0.1.0" @@ -251,6 +258,15 @@ dependencies = [ "cc", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" diff --git a/Cargo.toml b/Cargo.toml index edc6d40..1c79ddb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ chrono = { version = "0.4.38", optional = true } clippy = "0.0.302" dhat = { version = "0.3.3", optional = true } fmt = "0.1.0" +itertools = "0.13.0" pico-args = "0.5.0" regex = "1.11.1" tinyjson = "2.5.1" diff --git a/README.md b/README.md index 2a8b804..e1f68ce 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,15 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www. | Day | Part 1 | Part 2 | | :---: | :---: | :---: | -| [Day 1](./src/bin/01.rs) | `161.8µs` | `211.7µs` | -| [Day 2](./src/bin/02.rs) | `242.1µs` | `427.3µs` | -| [Day 3](./src/bin/03.rs) | `448.6µs` | `728.9µs` | -| [Day 4](./src/bin/04.rs) | `5.9ms` | `2.7ms` | -| [Day 5](./src/bin/05.rs) | `2.0ms` | `10.6ms` | -| [Day 6](./src/bin/06.rs) | `7.4ms` | `-` | +| [Day 1](./src/bin/01.rs) | `72.0µs` | `76.8µs` | +| [Day 2](./src/bin/02.rs) | `215.6µs` | `371.6µs` | +| [Day 3](./src/bin/03.rs) | `652.5µs` | `736.7µs` | +| [Day 4](./src/bin/04.rs) | `5.6ms` | `2.4ms` | +| [Day 5](./src/bin/05.rs) | `3.7ms` | `9.7ms` | +| [Day 6](./src/bin/06.rs) | `6.8ms` | `7.9s` | +| [Day 7](./src/bin/07.rs) | `43.5ms` | `3.7s` | -**Total: 30.82ms** +**Total: 11673.83ms** --- diff --git a/data/examples/07.txt b/data/examples/07.txt new file mode 100644 index 0000000..fc6e099 --- /dev/null +++ b/data/examples/07.txt @@ -0,0 +1,9 @@ +190: 10 19 +3267: 81 40 27 +83: 17 5 +156: 15 6 +7290: 6 8 6 15 +161011: 16 10 13 +192: 17 8 14 +21037: 9 7 18 13 +292: 11 6 16 20 diff --git a/src/bin/07.rs b/src/bin/07.rs new file mode 100644 index 0000000..c055e28 --- /dev/null +++ b/src/bin/07.rs @@ -0,0 +1,148 @@ +advent_of_code::solution!(7); + +use itertools::{repeat_n, Itertools}; + +#[derive(Debug)] +struct Calib { + res: u64, + numbers: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Ops { + Add, + Mul, + Concat, +} + +#[derive(Debug)] +enum Equ { + Nb(u64), + Op(u64, Ops, Box), +} + +impl Equ { + fn init(nbs: &[u64], ops: &[Ops]) -> Equ { + match (nbs, ops) { + ([h], []) => Equ::Nb(*h), + ([h, t @ ..], [op, top @ ..]) => Equ::Op(*h, *op, Box::new(Equ::init(t, top))), + _ => todo!(), + } + } + + fn add(&mut self, lhs: u64) -> u64 { + match self { + Equ::Nb(x) => return *x + lhs, + Equ::Op(llhs, _, _) => *llhs += lhs, + } + self.solve() + } + + fn mul(&mut self, lhs: u64) -> u64 { + match self { + Equ::Nb(x) => return *x * lhs, + Equ::Op(llhs, _, _) => *llhs *= lhs, + } + self.solve() + } + + fn concat(&mut self, lhs: u64) -> u64 { + match self { + Equ::Nb(x) => return format!("{}{}", lhs, x).parse::().unwrap(), + Equ::Op(x, _, _) => *x = format!("{}{}", lhs, x).parse::().unwrap(), + } + self.solve() + } + + fn solve(&mut self) -> u64 { + match self { + Equ::Nb(x) => *x, + Equ::Op(lhs, Ops::Add, rhs) => rhs.add(*lhs), + Equ::Op(lhs, Ops::Mul, rhs) => rhs.mul(*lhs), + Equ::Op(lhs, Ops::Concat, rhs) => rhs.concat(*lhs), + } + } +} + +impl Ops { + fn possible_ops_p1(l: usize) -> Vec> { + repeat_n(vec![Ops::Add, Ops::Mul].into_iter(), l - 1) + .multi_cartesian_product() + .collect() + } + fn possible_ops_p2(l: usize) -> Vec> { + repeat_n(vec![Ops::Add, Ops::Mul, Ops::Concat].into_iter(), l - 1) + .multi_cartesian_product() + .collect() + } +} + +impl TryFrom<&str> for Calib { + type Error = (); + fn try_from(input: &str) -> Result { + let mut numbers: Vec = input + .chars() + .filter(|c| *c != ':') + .collect::() + .split_whitespace() + .flat_map(|n| n.parse::()) + .collect(); + let res = numbers.remove(0); + Ok(Self { res, numbers }) + } +} + +impl Calib { + fn check_ops(&self, ops: Vec) -> bool { + let nbs = self.numbers.clone(); + let mut eq = Equ::init(nbs.as_slice(), ops.as_slice()); + let res = eq.solve(); + res == self.res + } + + fn solve_p1(&self) -> bool { + Ops::possible_ops_p1(self.numbers.len()) + .into_iter() + .any(|ops| self.check_ops(ops)) + } + fn solve_p2(&self) -> bool { + Ops::possible_ops_p2(self.numbers.len()) + .into_iter() + .any(|ops| self.check_ops(ops)) + } +} + +pub fn part_one(input: &str) -> Option { + input + .lines() + .flat_map(Calib::try_from) + .filter_map(|c| if c.solve_p1() { Some(c.res) } else { None }) + .sum::() + .into() +} + +pub fn part_two(input: &str) -> Option { + input + .lines() + .flat_map(Calib::try_from) + .filter_map(|c| if c.solve_p2() { Some(c.res) } else { None }) + .sum::() + .into() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(3749)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(11387)); + } +}