use std::collections::HashMap; use color_eyre::{eyre::eyre, Result}; use itertools::Itertools; type PairMap = HashMap<(char, char), char>; type PairCounts = HashMap<(char, char), isize>; pub fn solve() -> Result<()> { let input_data = include_str!("../../data/day_14.txt").trim(); println!("Day 14 Part 1: {}", part_1(input_data)?); println!("Day 14 Part 2: {}", part_2(input_data)?); Ok(()) } fn parse(input: &str) -> Result<(String, PairMap)> { let mut lines = input.lines(); let template = lines .next() .ok_or_else(|| eyre!("Invalid input: {}", input))? .to_string(); let mut pairs = HashMap::new(); for line in lines.skip(1) { let (a, b, c) = line .replace(" -> ", "") .chars() .collect_tuple() .ok_or_else(|| eyre!("Invalid line: {}", line))?; pairs.insert((a, b), c); } Ok((template, pairs)) } fn apply(counts: &PairCounts, pairs: &PairMap) -> PairCounts { let mut to_add = PairCounts::new(); for (pair, count) in counts { if let Some(character) = pairs.get(pair) { *to_add.entry((pair.0, *character)).or_default() += count; *to_add.entry((*character, pair.1)).or_default() += count; } } to_add } fn count_totals(counts: &PairCounts) -> HashMap { let mut totals = HashMap::new(); for ((_, b), count) in counts { // If I count the first character in the pair the end result ends up being // off by one...? *totals.entry(*b).or_default() += count; } totals } fn run(input: &str, steps: isize) -> Result { let (template, pairs) = parse(input)?; let mut counts = PairCounts::new(); for (a, b) in template.chars().tuple_windows() { *counts.entry((a, b)).or_default() += 1; } for _ in 0..steps { counts = apply(&counts, &pairs); } let totals = count_totals(&counts) .into_iter() .sorted_by(|(_, a), (_, b)| a.cmp(b)); let (_, min) = totals .clone() .next() .ok_or_else(|| eyre!("No minimum found"))?; let (_, max) = totals .clone() .last() .ok_or_else(|| eyre!("No maximum found"))?; Ok(max - min) } fn part_1(input: &str) -> Result { run(input, 10) } fn part_2(input: &str) -> Result { run(input, 40) }