diff --git a/source/day_14/mod.rs b/source/day_14/mod.rs new file mode 100644 index 0000000..43ec255 --- /dev/null +++ b/source/day_14/mod.rs @@ -0,0 +1,97 @@ +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) +} diff --git a/source/main.rs b/source/main.rs index 9c8c08f..3787895 100644 --- a/source/main.rs +++ b/source/main.rs @@ -12,6 +12,7 @@ mod day_07; mod day_08; mod day_09; mod day_10; +mod day_14; fn main() -> Result<()> { color_eyre::install()?; @@ -29,6 +30,7 @@ fn main() -> Result<()> { day_08::solve, day_09::solve, day_10::solve, + day_14::solve, ]; for day in days {