From 6d72191e2ce5d423ca03c894d2dad1d3061bd4f3 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Tue, 19 Apr 2022 20:27:29 +0200 Subject: Refile for merging repos --- 2021/src/bin/day_14.rs | 125 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 2021/src/bin/day_14.rs (limited to '2021/src/bin/day_14.rs') diff --git a/2021/src/bin/day_14.rs b/2021/src/bin/day_14.rs new file mode 100644 index 0000000..e757faf --- /dev/null +++ b/2021/src/bin/day_14.rs @@ -0,0 +1,125 @@ +use nom::{ + bytes::complete::tag, + character::complete::{alpha1, anychar, line_ending}, + combinator::map, + multi::{many0, separated_list1}, + sequence::tuple, + IResult, +}; +use std::{collections::BTreeMap, fs}; + +fn main() -> Result<(), Box> { + let input = fs::read_to_string("inputs/day_14.txt")?; + let mut polymer = parse_polymer_expansion(&input).unwrap().1; + + let small_expansion_counts = polymer.count_after_expansions(10); + dbg!(small_expansion_counts.most_common() - small_expansion_counts.least_common()); + + let big_expansion_counts = polymer.count_after_expansions(40); + dbg!(big_expansion_counts.most_common() - big_expansion_counts.least_common()); + + Ok(()) +} + +#[derive(Debug)] +struct PolymerExpansion { + polymer: Vec, + rules: Rules, + cache: BTreeMap<(char, char, usize), ElementCount>, +} + +impl PolymerExpansion { + fn new(polymer: Vec, rules: Rules) -> PolymerExpansion { + PolymerExpansion { + polymer, + rules, + cache: BTreeMap::new(), + } + } + + fn count_after_expansions(&mut self, expansions: usize) -> ElementCount { + let mut counts = ElementCount::default(); + for i in 0..self.polymer.len() - 1 { + counts.add(self.polymer[i]); + counts.append(self.count_between(self.polymer[i], self.polymer[i + 1], expansions)); + } + counts.add(self.polymer[self.polymer.len() - 1]); + counts + } + + fn count_between( + &mut self, + left: char, + right: char, + remaining_expansions: usize, + ) -> ElementCount { + if remaining_expansions == 0 { + ElementCount::default() + } else if let Some(cached) = self.cache.get(&(left, right, remaining_expansions)) { + cached.clone() + } else { + let mut counts = ElementCount::default(); + let middle = self.rules.get(&[left, right]).expect("No rule!"); + counts.add(middle); + counts.append(self.count_between(left, middle, remaining_expansions - 1)); + counts.append(self.count_between(middle, right, remaining_expansions - 1)); + self.cache + .insert((left, right, remaining_expansions), counts.clone()); + counts + } + } +} + +#[derive(Debug, Default, Clone)] +struct ElementCount(BTreeMap); + +impl ElementCount { + fn add(&mut self, c: char) { + *self.0.entry(c).or_insert(0) += 1; + } + + fn append(&mut self, other: ElementCount) { + for (key, val) in other.0 { + *self.0.entry(key).or_insert(0) += val; + } + } + + fn most_common(&self) -> u64 { + self.0.values().max().cloned().unwrap_or(0) + } + + fn least_common(&self) -> u64 { + self.0.values().min().cloned().unwrap_or(0) + } +} + +#[derive(Debug)] +struct Rules { + rules: BTreeMap<[char; 2], char>, +} + +impl Rules { + fn get(&self, key: &[char; 2]) -> Option { + self.rules.get(key).cloned() + } +} + +fn parse_polymer_expansion(input: &str) -> IResult<&str, PolymerExpansion> { + let (input, polymer) = map(alpha1, |s: &str| s.chars().collect())(input)?; + let (input, _) = many0(line_ending)(input)?; + let (input, rules) = parse_rules(input)?; + Ok((input, PolymerExpansion::new(polymer, rules))) +} + +fn parse_rules(input: &str) -> IResult<&str, Rules> { + map(separated_list1(line_ending, parse_rule), |rules| Rules { + rules: rules.into_iter().collect(), + })(input) +} + +fn parse_rule(input: &str) -> IResult<&str, ([char; 2], char)> { + map( + tuple((anychar, anychar, tag(" -> "), anychar)), + |(p1, p2, _, r)| ([p1, p2], r), + )(input) +} -- cgit v1.2.3