1
0

feat: solve day 14 part 2

This commit is contained in:
Rokas Puzonas 2021-12-27 19:12:20 +02:00
parent 42ea48d4e9
commit dccffe6ab1
2 changed files with 111 additions and 38 deletions

View File

@ -1,39 +1,34 @@
use std::collections::HashMap; use std::collections::HashMap;
pub struct InsertionRule([char; 2], char);
pub struct InputData { pub struct InputData {
polymer_template: String, polymer_template: String,
rules: Vec<InsertionRule> rules: HashMap<[char; 2], char>
}
fn parse_rule(line: &str) -> InsertionRule {
let (pattern, expansion) = line.split_once(" -> ").unwrap();
return InsertionRule(
[pattern.chars().nth(0).unwrap(), pattern.chars().nth(1).unwrap()],
expansion.chars().nth(0).unwrap()
);
} }
pub fn parse_input(input: &str) -> InputData { pub fn parse_input(input: &str) -> InputData {
let (polymer_template, rules_section) = input.split_once("\n\n").unwrap(); let (polymer_template, rules_section) = input.split_once("\n\n").unwrap();
let rules = rules_section.lines() let mut rules = HashMap::new();
.map(parse_rule) for line in rules_section.lines() {
.collect(); let (pattern, expansion) = line.split_once(" -> ").unwrap();
rules.insert(
[pattern.chars().nth(0).unwrap(), pattern.chars().nth(1).unwrap()],
expansion.chars().nth(0).unwrap()
);
}
return InputData { return InputData {
polymer_template: polymer_template.into(), polymer_template: polymer_template.into(),
rules rules
}; };
} }
fn expand_polymer(polymer: Vec<char>, rules: &Vec<InsertionRule>) -> Vec<char> { fn naive_expand_polymer(polymer: Vec<char>, rules: &HashMap<[char; 2], char>) -> Vec<char> {
let mut new_polymer = Vec::new(); let mut new_polymer = Vec::new();
for i in 0..polymer.len()-1 { for i in 0..polymer.len()-1 {
new_polymer.push(polymer[i]); new_polymer.push(polymer[i]);
for rule in rules { let pair = [polymer[i], polymer[i+1]];
if rule.0[0] == polymer[i] && rule.0[1] == polymer[i+1] { let rule = rules.get(&pair);
new_polymer.push(rule.1); if rule != None {
} new_polymer.push(*rule.unwrap());
} }
} }
new_polymer.push(*polymer.last().unwrap()); new_polymer.push(*polymer.last().unwrap());
@ -43,7 +38,7 @@ fn expand_polymer(polymer: Vec<char>, rules: &Vec<InsertionRule>) -> Vec<char> {
pub fn part1(input: &InputData) -> u32 { pub fn part1(input: &InputData) -> u32 {
let mut polymer = input.polymer_template.chars().collect(); let mut polymer = input.polymer_template.chars().collect();
for _ in 0..10 { for _ in 0..10 {
polymer = expand_polymer(polymer, &input.rules); polymer = naive_expand_polymer(polymer, &input.rules);
} }
let mut element_amounts = HashMap::new(); let mut element_amounts = HashMap::new();
@ -57,6 +52,58 @@ pub fn part1(input: &InputData) -> u32 {
return most_common_element - least_common_element; return most_common_element - least_common_element;
} }
fn expand_polymer(polymer_pairs: &HashMap<[char; 2], u64>, rules: &HashMap<[char; 2], char>) -> HashMap<[char; 2], u64> {
let mut new_pairs = HashMap::new();
for entry in polymer_pairs {
let pair = entry.0;
if rules.contains_key(pair) {
let rule = *rules.get(pair).unwrap();
let left_pair = [pair[0], rule];
let left_entry = new_pairs.entry(left_pair).or_insert(0);
*left_entry += entry.1;
let right_pair = [rule, pair[1]];
let right_entry = new_pairs.entry(right_pair).or_insert(0);
*right_entry += entry.1;
} else {
let new_entry = new_pairs.entry(*entry.0).or_insert(0);
*new_entry = *entry.1;
}
}
return new_pairs;
}
pub fn part2(input: &InputData) -> u64 {
let polymer_template = &input.polymer_template;
let mut polymer_pairs = HashMap::new();
for i in 0..polymer_template.len()-1 {
let pair = [
polymer_template.chars().nth(i).unwrap(),
polymer_template.chars().nth(i+1).unwrap()
];
let entry = polymer_pairs.entry(pair).or_insert(0);
*entry += 1;
}
for _ in 0..40 {
polymer_pairs = expand_polymer(&polymer_pairs, &input.rules);
}
let mut element_amounts = HashMap::new();
for entry in polymer_pairs {
for c in entry.0 {
let amount = element_amounts.entry(c).or_insert(0);
*amount += entry.1;
}
}
let least_common_element = element_amounts.iter().min_by(|a, b| a.1.cmp(b.1)).unwrap().1;
let most_common_element = element_amounts.iter().max_by(|a, b| a.1.cmp(b.1)).unwrap().1;
return (most_common_element - least_common_element)/2 + 1;
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -64,25 +111,50 @@ mod tests {
#[test] #[test]
fn part1_example() { fn part1_example() {
let polymer_template = String::from("NNCB"); let polymer_template = String::from("NNCB");
let rules = vec![ let rules = HashMap::from([
InsertionRule(['C', 'H'], 'B'), (['C', 'H'], 'B'),
InsertionRule(['H', 'H'], 'N'), (['H', 'H'], 'N'),
InsertionRule(['C', 'B'], 'H'), (['C', 'B'], 'H'),
InsertionRule(['N', 'H'], 'C'), (['N', 'H'], 'C'),
InsertionRule(['H', 'B'], 'C'), (['H', 'B'], 'C'),
InsertionRule(['H', 'C'], 'B'), (['H', 'C'], 'B'),
InsertionRule(['H', 'N'], 'C'), (['H', 'N'], 'C'),
InsertionRule(['N', 'N'], 'C'), (['N', 'N'], 'C'),
InsertionRule(['B', 'H'], 'H'), (['B', 'H'], 'H'),
InsertionRule(['N', 'C'], 'B'), (['N', 'C'], 'B'),
InsertionRule(['N', 'B'], 'B'), (['N', 'B'], 'B'),
InsertionRule(['B', 'N'], 'B'), (['B', 'N'], 'B'),
InsertionRule(['B', 'B'], 'N'), (['B', 'B'], 'N'),
InsertionRule(['B', 'C'], 'B'), (['B', 'C'], 'B'),
InsertionRule(['C', 'C'], 'N'), (['C', 'C'], 'N'),
InsertionRule(['C', 'N'], 'C') (['C', 'N'], 'C')
]; ]);
let result = part1(&InputData { polymer_template, rules }); let result = part1(&InputData { polymer_template, rules });
assert_eq!(result, 1588); assert_eq!(result, 1588);
} }
#[test]
fn part2_example() {
let polymer_template = String::from("NNCB");
let rules = HashMap::from([
(['C', 'H'], 'B'),
(['H', 'H'], 'N'),
(['C', 'B'], 'H'),
(['N', 'H'], 'C'),
(['H', 'B'], 'C'),
(['H', 'C'], 'B'),
(['H', 'N'], 'C'),
(['N', 'N'], 'C'),
(['B', 'H'], 'H'),
(['N', 'C'], 'B'),
(['N', 'B'], 'B'),
(['B', 'N'], 'B'),
(['B', 'B'], 'N'),
(['B', 'C'], 'B'),
(['C', 'C'], 'N'),
(['C', 'N'], 'C')
]);
let result = part2(&InputData { polymer_template, rules });
assert_eq!(result, 2188189693529);
}
} }

View File

@ -55,6 +55,7 @@ fn run(day: i32, part: i32, input_filename: &str) {
"13.1" => println!("{}", day13::part1(&day13::parse_input(&contents))), "13.1" => println!("{}", day13::part1(&day13::parse_input(&contents))),
"13.2" => day13::part2(&day13::parse_input(&contents)), "13.2" => day13::part2(&day13::parse_input(&contents)),
"14.1" => println!("{}", day14::part1(&day14::parse_input(&contents))), "14.1" => println!("{}", day14::part1(&day14::parse_input(&contents))),
"14.2" => println!("{}", day14::part2(&day14::parse_input(&contents))),
_ => println!("Day {} part {} not found", day, part) _ => println!("Day {} part {} not found", day, part)
} }
} }