From 37293ab29e5abf1451cec7a85652bff5eee324a7 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Wed, 15 Dec 2021 19:54:39 +0100 Subject: [PATCH] 2021 day14/rust: add solution --- 2021/day14/day14_rs/Cargo.toml | 9 ++ 2021/day14/day14_rs/src/main.rs | 152 ++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 2021/day14/day14_rs/Cargo.toml create mode 100644 2021/day14/day14_rs/src/main.rs diff --git a/2021/day14/day14_rs/Cargo.toml b/2021/day14/day14_rs/Cargo.toml new file mode 100644 index 0000000..0984fea --- /dev/null +++ b/2021/day14/day14_rs/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "day14_rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +nom = "7.1.0" diff --git a/2021/day14/day14_rs/src/main.rs b/2021/day14/day14_rs/src/main.rs new file mode 100644 index 0000000..77fef7e --- /dev/null +++ b/2021/day14/day14_rs/src/main.rs @@ -0,0 +1,152 @@ +#![warn(clippy::pedantic)] +use nom::{ + bytes::complete::{tag, take_till}, + character::complete::{anychar, newline}, + combinator::recognize, + multi::many1, + sequence::{pair, separated_pair, terminated}, +}; +use std::collections::BTreeMap; +use std::io::{stdin, Read}; +use std::ops::Add; + +type Input<'a> = &'a str; +type IResult<'a, T> = nom::IResult, T>; + +type Rule = ((char, char), char); + +fn rule(i: Input) -> IResult { + separated_pair(pair(anychar, anychar), tag(" -> "), anychar)(i) +} + +fn parse_input(i: Input) -> IResult<(&str, Vec)> { + separated_pair( + terminated(recognize(take_till(|c| c == '\n')), newline), + newline, + many1(terminated(rule, newline)), + )(i) +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct Counter { + counts: BTreeMap, +} + +impl Counter { + pub fn new() -> Self { + Self { + counts: BTreeMap::new(), + } + } + + pub fn push(&mut self, el: T) { + *self.counts.entry(el).or_insert(0) += 1; + } + + pub fn most_common(&self) -> Option<(&T, usize)> { + self.counts + .iter() + .map(|(el, count)| (el, *count)) + .max_by_key(|&(_, count)| count) + } + + pub fn least_common(&self) -> Option<(&T, usize)> { + self.counts + .iter() + .map(|(el, count)| (el, *count)) + .min_by_key(|&(_, count)| count) + } +} + +impl FromIterator for Counter { + fn from_iter>(iter: I) -> Self { + let mut counts = Self::new(); + for el in iter { + counts.push(el); + } + counts + } +} + +impl Add<&Counter> for Counter { + type Output = Self; + + fn add(mut self, other: &Self) -> Self { + for (el, count) in &other.counts { + *self.counts.entry(*el).or_insert(0) += count; + } + self + } +} + +impl Add for Counter { + type Output = Self; + + fn add(mut self, other: Self) -> Self { + for (el, count) in other.counts { + *self.counts.entry(el).or_insert(0) += count; + } + self + } +} + +#[derive(Clone, Debug)] +struct LetterCounter { + rules: BTreeMap<(char, char), char>, + counts: BTreeMap<(char, char, usize), Counter>, +} + +impl LetterCounter { + pub fn new(rules: BTreeMap<(char, char), char>) -> Self { + let mut counts = BTreeMap::new(); + for &(left, right) in rules.keys() { + counts.insert((left, right, 0), Counter::from_iter([left])); + } + Self { rules, counts } + } + + pub fn get_counts_right_exclusive( + &mut self, + left: char, + right: char, + depth: usize, + ) -> &Counter { + #[allow(clippy::map_entry)] // lifetimes don't work out + if !self.counts.contains_key(&(left, right, depth)) { + let middle = self.rules[&(left, right)]; + let counts_left = self + .get_counts_right_exclusive(left, middle, depth - 1) + .clone(); + let counts_right = self.get_counts_right_exclusive(middle, right, depth - 1); + let counts = counts_left + counts_right; + self.counts.insert((left, right, depth), counts); + } + &self.counts[&(left, right, depth)] + } +} + +fn main() { + let mut input = String::new(); + stdin().lock().read_to_string(&mut input).unwrap(); + + let (input, rules) = parse_input(&input).unwrap().1; + + let rules: BTreeMap<_, _> = rules.into_iter().collect(); + let chars: Vec<_> = input.chars().collect(); + + let mut counter = LetterCounter::new(rules); + + let mut run = |steps| { + let mut totals = chars.windows(2).fold(Counter::new(), |counts, x| { + counts + counter.get_counts_right_exclusive(x[0], x[1], steps) + }); + totals.push(*chars.last().unwrap()); + + println!( + "{:?}", + totals.most_common().unwrap().1 - totals.least_common().unwrap().1 + ); + }; + run(10); + run(40); +}