From 2c499e9efebe57cddd01017514c59a20ff5cfad6 Mon Sep 17 00:00:00 2001 From: Andrew Glaze Date: Thu, 28 Dec 2023 13:35:25 -0500 Subject: [PATCH] day7 --- Cargo.lock | 52 +++++++++++++++++++++++ Cargo.toml | 2 + src/day7.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 3 ++ 4 files changed, 176 insertions(+) create mode 100644 src/day7.rs diff --git a/Cargo.lock b/Cargo.lock index 222422e..f686c5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,10 +9,21 @@ dependencies = [ "aoc-runner", "aoc-runner-derive", "itertools", + "lazy_static", + "regex", "strum", "strum_macros", ] +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + [[package]] name = "aoc-runner" version = "0.3.0" @@ -69,6 +80,18 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + [[package]] name = "proc-macro2" version = "1.0.71" @@ -87,6 +110,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + [[package]] name = "rustversion" version = "1.0.14" diff --git a/Cargo.toml b/Cargo.toml index de63573..6039a93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,5 @@ aoc-runner-derive = "0.3.0" itertools = "0.12.0" strum = "0.25.0" strum_macros = "0.25" +lazy_static = "1.4.0" +regex = "1.10.2" \ No newline at end of file diff --git a/src/day7.rs b/src/day7.rs new file mode 100644 index 0000000..4063d40 --- /dev/null +++ b/src/day7.rs @@ -0,0 +1,119 @@ +use std::collections::{HashMap, VecDeque}; + +use aoc_runner_derive::{aoc, aoc_generator}; +use regex::Regex; + +const GOAL: &str = "shiny gold"; + +lazy_static! { + static ref LINE_RE: Regex = Regex::new(r"(\w+ \w+) bags contain (.*)").unwrap(); + static ref ITEM_RE: Regex = Regex::new(r"(\d+) (\w+ \w+) bags?").unwrap(); +} + +#[aoc_generator(day7)] +fn parse(input: &str) -> HashMap> { + let mut bags = HashMap::>::new(); + for line in input.lines() { + if let Some((item, items)) = LINE_RE + .captures(line.as_ref()) + .and_then(|captures| { + Some((captures.get(1)?.as_str(), captures.get(2)?.as_str())) + }) { + bags.insert( + item.to_string(), + ITEM_RE + .captures_iter(items) + .filter_map(|captures| { + Some(( + captures.get(1)?.as_str().parse().ok()?, + captures.get(2)?.as_str().to_string(), + )) + }).collect() + ); + } + + } + bags +} + +struct Expand<'a> { + bags: &'a HashMap>, + queue: VecDeque<(usize, &'a str)> +} + +fn expand<'a>(bags: &'a HashMap>, bag: &str) -> Expand<'a> { + Expand { + bags, + queue: bags.get(bag).map_or_else(VecDeque::new, |items| { + items.iter() + .map(|(count, item)| (*count, item.as_str())) + .collect() + }), + } +} + +impl<'a> Iterator for Expand<'a> { + type Item = (usize, &'a str); + + fn next(&mut self) -> Option { + self.queue.pop_front().map(|(count, item)| { + if let Some(items) = self.bags.get(item) { + for (subcount, subitem) in items { + self.queue.push_back((count * subcount, subitem)); + } + } + (count, item) + }) + } +} + +#[aoc(day7, part1)] +fn part1(bags: &HashMap>) -> usize { + bags.keys() + .filter(|key| expand(&bags, key).any(|(_, item)| item == GOAL)) + .count() +} + +#[aoc(day7, part2)] +fn part2(bags: &HashMap>) -> usize { + expand(bags, GOAL).map(|(count, _)| count).sum() +} + + +#[cfg(test)] +mod tests { + use super::*; + +const EX: &str = r"light red bags contain 1 bright white bag, 2 muted yellow bags. +dark orange bags contain 3 bright white bags, 4 muted yellow bags. +bright white bags contain 1 shiny gold bag. +muted yellow bags contain 2 shiny gold bags, 9 faded blue bags. +shiny gold bags contain 1 dark olive bag, 2 vibrant plum bags. +dark olive bags contain 3 faded blue bags, 4 dotted black bags. +vibrant plum bags contain 5 faded blue bags, 6 dotted black bags. +faded blue bags contain no other bags. +dotted black bags contain no other bags."; + +const EX_2: &str = r"shiny gold bags contain 2 dark red bags. +dark red bags contain 2 dark orange bags. +dark orange bags contain 2 dark yellow bags. +dark yellow bags contain 2 dark green bags. +dark green bags contain 2 dark blue bags. +dark blue bags contain 2 dark violet bags. +dark violet bags contain no other bags."; + + #[test] + fn part1_example() { + assert_eq!(part1(&parse(EX)), 4); + } + + #[test] + fn part2_example() { + assert_eq!(part2(&parse(EX)), 32); + } + + #[test] + fn part2_example2() { + assert_eq!(part2(&parse(EX_2)), 126); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b565cdc..f122e7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +mod day7; mod day6; mod day5; mod day1; @@ -8,5 +9,7 @@ extern crate aoc_runner; #[macro_use] extern crate aoc_runner_derive; +#[macro_use] +extern crate lazy_static; aoc_lib!{ year = 2020 } \ No newline at end of file