Start catching up after vacation.

This commit is contained in:
2021-12-19 19:01:52 -08:00
parent aa1bc0ee49
commit 1f286b293e
17 changed files with 1816 additions and 3 deletions

183
src/bin/day14.rs Normal file
View File

@@ -0,0 +1,183 @@
use std::collections::HashMap;
use thiserror::Error;
const TEST_DATA: &str = include_str!("../../data/day14t.txt");
const REAL_DATA: &str = include_str!("../../data/day14a.txt");
#[derive(Debug, Error, PartialEq)]
enum Oopsie {
#[error("Bad transform declaration: {0}")]
BadTransform(String),
#[error("No data found?!")]
NoData,
}
struct Transform {
lead: char,
follow: char,
inject: char,
}
impl TryFrom<&str> for Transform {
type Error = Oopsie;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s.split_once(" -> ") {
None => Err(Oopsie::BadTransform(s.to_string())),
Some((from, to_str)) => Ok(Transform {
lead: from.chars().next().unwrap(),
follow: from.chars().nth(1).unwrap(),
inject: to_str.chars().next().unwrap(),
}),
}
}
}
fn read_file(contents: &str) -> Result<(String, Vec<Transform>), Oopsie> {
let mut lines = contents.lines();
let mut transforms = Vec::new();
let base_str = match lines.next() {
None => return Err(Oopsie::NoData),
Some(x) => x.to_string(),
};
for line in lines {
if line.is_empty() {
continue;
}
transforms.push(Transform::try_from(line)?);
}
Ok((base_str, transforms))
}
type Pair = (char, char);
type TransformDictionary = HashMap<Pair, [Pair; 2]>;
fn build_transform_dictionary(mut transforms: Vec<Transform>) -> TransformDictionary {
let mut result = HashMap::with_capacity(transforms.len());
for transform in transforms.drain(..) {
result.insert(
(transform.lead, transform.follow),
[
(transform.lead, transform.inject),
(transform.inject, transform.follow),
],
);
}
result
}
type State = HashMap<Pair, usize>;
macro_rules! insert_update {
($dict: expr, $key: expr, $value: expr, |$x: ident| $body: expr) => {
match $dict.get_mut(&$key) {
None => {
$dict.insert($key, $value);
}
Some($x) => *$x = $body,
}
};
}
fn build_initial_state(base: &str) -> State {
let mut state = HashMap::new();
let mut chars = base.chars().peekable();
while let Some(c1) = chars.next() {
match chars.peek() {
None => break,
Some(c2) => insert_update!(state, (c1, *c2), 1, |v| *v + 1),
}
}
state
}
fn step(base: &State, transforms: &TransformDictionary) -> State {
let mut result = HashMap::new();
for (pair, count) in base.iter() {
match transforms.get(pair) {
None => insert_update!(result, *pair, *count, |v| *v + count),
Some([p1, p2]) => {
insert_update!(result, *p1, *count, |v| *v + count);
insert_update!(result, *p2, *count, |v| *v + count);
}
}
}
result
}
fn steps(base: &State, steps: usize, transforms: &TransformDictionary) -> State {
let mut result = base.clone();
for i in 0..steps {
println!("At step {}, length is {}", i, result.len());
result = step(&result, transforms);
}
result
}
fn counts(polymer: &State) -> HashMap<char, usize> {
let mut char_counts = HashMap::new();
for ((c1, c2), count) in polymer.iter() {
insert_update!(char_counts, *c1, *count, |v| *v + count);
insert_update!(char_counts, *c2, *count, |v| *v + count);
}
char_counts
}
fn score(map: &HashMap<char, usize>) -> usize {
let high = map.values().max().unwrap();
let low = map.values().min().unwrap();
(high - low) / 2
}
fn main() -> Result<(), Oopsie> {
let (test_base, test_transforms) = read_file(TEST_DATA)?;
let (real_base, real_transforms) = read_file(REAL_DATA)?;
let test_initial = build_initial_state(&test_base);
let real_initial = build_initial_state(&real_base);
let test_transform_dict = build_transform_dictionary(test_transforms);
let real_transform_dict = build_transform_dictionary(real_transforms);
println!("test base: {:?}", test_initial);
println!("one step: {:?}", step(&test_initial, &test_transform_dict));
println!(
"four steps: {:?}",
steps(&test_initial, 4, &test_transform_dict)
);
println!(
"Length at 10 steps: {:?}",
steps(&test_initial, 10, &test_transform_dict).len()
);
println!(
"Counts at 10 steps: {:?}",
counts(&steps(&test_initial, 10, &test_transform_dict))
);
println!(
"Score at 10 steps: {:?}",
score(&counts(&steps(&test_initial, 10, &test_transform_dict)))
);
println!(
"Real score at 10 steps: {:?}",
score(&counts(&steps(&real_initial, 10, &real_transform_dict)))
);
println!(
"Real score at 40 steps: {:?}",
score(&counts(&steps(&real_initial, 40, &real_transform_dict)))
);
Ok(())
}