From 4a54a0d97a57df6c1525b9bbfcabdf829e44a4d1 Mon Sep 17 00:00:00 2001 From: cos Date: Tue, 14 Dec 2021 09:00:15 +0100 Subject: Add day14, 2021 --- 2021/rust/day14/Cargo.toml | 10 ++ 2021/rust/day14/src/main.rs | 234 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 244 insertions(+) create mode 100644 2021/rust/day14/Cargo.toml create mode 100644 2021/rust/day14/src/main.rs (limited to '2021/rust/day14') diff --git a/2021/rust/day14/Cargo.toml b/2021/rust/day14/Cargo.toml new file mode 100644 index 0000000..ce7a90b --- /dev/null +++ b/2021/rust/day14/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "day14" +version = "0.1.0" +authors = ["cos "] +edition = "2021" + +[dependencies] +aoc = { path = "../../../common/rust/aoc" } +anyhow = "1.0" +regex = "1.5" diff --git a/2021/rust/day14/src/main.rs b/2021/rust/day14/src/main.rs new file mode 100644 index 0000000..a3b1e40 --- /dev/null +++ b/2021/rust/day14/src/main.rs @@ -0,0 +1,234 @@ +use { + anyhow::{ + anyhow, + Context, + Result, + }, + regex::Regex, + std::{ + collections::HashMap, + env::args, + fs::File, + io::{ + BufRead, + BufReader, + }, + path::Path, + }, +}; + +#[derive(Debug)] +struct InsertionRule { + pair: String, + element: String, +} + +impl InsertionRule { + fn new(pair: &str, element: &str) -> Result { + Ok(Self{ + pair: pair.into(), + element: element.into(), + }) + } + + fn new_apply(&self, input: &str) -> Result { + let mut iter = input.chars().take(2); + let (left, right) = ( + iter.next().ok_or_else(|| anyhow!("Failure on pair: {}", input))?, + iter.next().ok_or_else(|| anyhow!("Failure on pair: {}", input))?, + ); + let mut output = format!("{}", left); + + if input.starts_with(&self.pair) { + output += &format!("{}", self.element.chars().next() + .ok_or_else(|| anyhow!("Failure with element for: {}", input))?); + } + output += &format!("{}", right); + Ok(output) + } +} + +#[derive(Debug)] +struct Polymer { + inner: String, +} + +type CharCount = HashMap; +type CacheMap = HashMap; + +impl Polymer { + fn new(template: impl ToString) -> Self { + Self { + inner: template.to_string(), + } + } + + fn len(&self) -> usize { + self.inner.len() + } + + fn first(&self) -> Result { + self.inner.chars().next().ok_or_else(|| anyhow!("Could not get first character")) + } + + fn transform<'a, I>(&self, rules: I) -> Result + where I: Copy + IntoIterator + { + let first = if let Some(c) = self.inner.chars().next() { + c.to_string() + } else { + String::from("") + }; + let inner = format!("{}{}", first, + self.inner.chars().zip(self.inner.chars().skip(1)).map(|(left, right)| { + let input = format!("{}{}", left, right); + for rule in rules { + let output = rule.new_apply(&input)?; + if input != output { + return Ok(output[1..].to_string()); + } + } + Ok(input[1..].to_string()) + }).collect::>()?); + Ok(Self { + inner + }) + } + + fn elements(&self) -> HashMap { + let mut elements = HashMap::new(); + + for c in self.inner.chars() { + let entry = elements.entry(c).or_insert(0); + *entry += 1; + } + + elements + } + + fn str_to_char_count(s: &str) -> HashMap { + let mut elements = HashMap::new(); + for c in s.chars() { + let entry = elements.entry(c).or_insert(0); + *entry += 1; + } + + elements + } + + fn evaluate<'a, I>(&self, rules: I, depth: usize, skip_first: bool, cache: &mut CacheMap) + -> Result + where I: Copy + IntoIterator + { + if depth == 0 { + let interesting = if skip_first { + &self.inner[1..] + } else { + &self.inner + }; + return Ok(Self::str_to_char_count(interesting)); + } + let mut char_count = CharCount::new(); + if !skip_first { + char_count.insert(self.first()?, 1); + } + + for index in 0..=(self.len() - 2) { + let sub_key = self.inner[index..index+2].to_string(); + let cache_key = &format!("{}-{}-{:?}", sub_key, depth, skip_first); + if let Some(cached) = cache.get(cache_key) { + for (key, value) in cached.iter() { + let entry = char_count.entry(*key).or_insert(0); + *entry += value; + } + } else { + let sub_polymer = Polymer::new(&sub_key); + let transformed = sub_polymer.transform(rules)?; + + let polymer_chars = &transformed.evaluate(rules, depth - 1, true, cache)?; + cache.insert(cache_key.to_string(), polymer_chars.clone()); + for (key, value) in polymer_chars.iter() { + let entry = char_count.entry(*key).or_insert(0); + *entry += value; + } + } + } + Ok(char_count) + } +} + +fn read_input>(filename: T) -> Result<(Polymer, Vec)> { + let reader = BufReader::new(File::open(filename)?); + let mut polymer = None; + let mut rules = vec![]; + + let re = Regex::new(r#"(?x) + ^(?P