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) }