From 868c3e56fc971baff53998332f77d84ef6e46208 Mon Sep 17 00:00:00 2001 From: Keenan Tims Date: Fri, 6 Dec 2024 21:40:25 -0800 Subject: [PATCH] day7: complete solution --- 7/Cargo.lock | 25 ++++++++ 7/Cargo.toml | 7 +++ 7/src/main.rs | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 7/Cargo.lock create mode 100644 7/Cargo.toml create mode 100644 7/src/main.rs diff --git a/7/Cargo.lock b/7/Cargo.lock new file mode 100644 index 0000000..5746643 --- /dev/null +++ b/7/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day7" +version = "0.1.0" +dependencies = [ + "itertools", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] diff --git a/7/Cargo.toml b/7/Cargo.toml new file mode 100644 index 0000000..3bc0606 --- /dev/null +++ b/7/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day7" +version = "0.1.0" +edition = "2021" + +[dependencies] +itertools = "0.13.0" diff --git a/7/src/main.rs b/7/src/main.rs new file mode 100644 index 0000000..c388fab --- /dev/null +++ b/7/src/main.rs @@ -0,0 +1,169 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Lines}; +use std::time::{Duration, Instant}; + +use itertools::Itertools; + +// BOILERPLATE +type InputIter = Lines>; + +pub fn get_input() -> InputIter { + let f = File::open("input").unwrap(); + let br = BufReader::new(f); + br.lines() +} + +fn duration_format(duration: Duration) -> String { + match duration.as_secs_f64() { + x if x > 1.0 => format!("{:.3}s", x), + x if x > 0.010 => format!("{:.3}ms", x * 1e3), + x => format!("{:.3}us", x * 1e6), + } +} + +fn main() { + let input = get_input(); + let start = Instant::now(); + let ans1 = problem1(input); + let duration1 = start.elapsed(); + println!("Problem 1 solution: {} [{}]", ans1, duration_format(duration1)); + + let input = get_input(); + let start = Instant::now(); + let ans2 = problem2(input); + let duration2 = start.elapsed(); + println!("Problem 2 solution: {} [{}]", ans2, duration_format(duration2)); + println!("Total duration: {}", duration_format(duration1 + duration2)); +} + +#[derive(Debug)] +struct Calibration { + result: u64, + numbers: Vec, +} + +impl From<&str> for Calibration { + fn from(value: &str) -> Self { + let (result, rest) = value.splitn(2, ':').next_tuple().unwrap(); + Self { + result: result.parse().unwrap(), + numbers: rest.split_ascii_whitespace().map(|s| s.parse().unwrap()).collect(), + } + } +} + +#[derive(Debug)] +struct Calibrations { + cals: Vec, +} + +#[derive(Debug)] +enum Operator { + Add, + Multiply, + Concatenate, +} + +impl Operator { + fn exec(&self, a: u64, b: u64) -> u64 { + match self { + Operator::Add => a + b, + Operator::Multiply => a * b, + Operator::Concatenate => u64::pow(10, b.to_string().len() as u32) * a + b, + } + } +} + +impl From> for Calibrations { + fn from(input: Lines) -> Self { + let mut cals = Vec::new(); + for line in input.map(|l| l.unwrap()) { + cals.push(line.as_str().into()); + } + Self { cals } + } +} + +// PROBLEM 1 solution + +fn problem1(input: Lines) -> u64 { + let cals = Calibrations::from(input); + // println!("{:?}", cals); + let mut sum = 0; + + let operators = [Operator::Add, Operator::Multiply]; + + for cal in &cals.cals { + let n_opers = cal.numbers.len() - 1; + // println!("CAL: {:?} (opers: {})", cal, n_opers); + for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() { + // println!("operator set: {:?}", oper_set); + let mut accum = cal.numbers[0]; + for (i, oper) in oper_set.iter().enumerate() { + // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]); + accum = oper.exec(accum, cal.numbers[i + 1]); + } + if accum == cal.result { + sum += cal.result; + // println!("Matched!"); + break; + } + } + // println!("NO MATCHES"); + } + sum +} + +// PROBLEM 2 solution +fn problem2(input: Lines) -> u64 { + let cals = Calibrations::from(input); + let mut sum = 0; + let operators = [Operator::Add, Operator::Multiply, Operator::Concatenate]; + for cal in &cals.cals { + let n_opers = cal.numbers.len() - 1; + // println!("CAL: {:?} (opers: {})", cal, n_opers); + for oper_set in std::iter::repeat_n(operators.iter(), n_opers).multi_cartesian_product() { + // println!("operator set: {:?}", oper_set); + let mut accum = cal.numbers[0]; + for (i, oper) in oper_set.iter().enumerate() { + // println!("Testing {} {:?} {}", accum, oper, cal.numbers[i+1]); + accum = oper.exec(accum, cal.numbers[i + 1]); + } + if accum == cal.result { + sum += cal.result; + // println!("Matched!"); + break; + } + } + // println!("NO MATCHES"); + } + sum +} + +#[cfg(test)] +mod tests { + use crate::*; + use std::io::Cursor; + + const EXAMPLE: &str = &"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"; + + #[test] + fn problem1_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem1(c.lines()), 3749); + } + + #[test] + fn problem2_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem2(c.lines()), 11387); + } +}