mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
124 lines
3.2 KiB
Rust
124 lines
3.2 KiB
Rust
use std::io::Read;
|
|
|
|
use itertools::Itertools;
|
|
use nom::bytes::complete::tag;
|
|
use nom::bytes::complete::take_while;
|
|
use nom::character::is_alphabetic;
|
|
use nom::multi::many0;
|
|
use nom::sequence::preceded;
|
|
use nom::sequence::terminated;
|
|
use nom::sequence::tuple;
|
|
use nom::Finish;
|
|
use nom::IResult;
|
|
|
|
type Rule = (u8, u8, u8);
|
|
|
|
type Pairs = [[u64; 26]; 26];
|
|
type Rules = [[u8; 26]; 26];
|
|
|
|
fn parse_input(input: &[u8]) -> IResult<&[u8], (&[u8], Vec<Rule>)> {
|
|
use nom::character::complete::char;
|
|
use nom::number::complete::u8;
|
|
|
|
let parse_start = take_while(is_alphabetic);
|
|
let parse_rule = tuple((u8, u8, preceded(tag(" -> "), u8)));
|
|
|
|
tuple((
|
|
parse_start,
|
|
preceded(tag("\n\n"), many0(terminated(parse_rule, char('\n')))),
|
|
))(input)
|
|
}
|
|
|
|
fn read_input(input: &mut dyn Read) -> (u8, u8, Pairs, Rules) {
|
|
let mut buffer = Vec::new();
|
|
input.read_to_end(&mut buffer).unwrap();
|
|
|
|
let (initial, rules) = parse_input(&buffer).finish().unwrap().1;
|
|
|
|
let mut pairs = Pairs::default();
|
|
|
|
for window in initial.windows(2) {
|
|
pairs[(window[0] - b'A') as usize][(window[1] - b'A') as usize] += 1;
|
|
}
|
|
|
|
let mut rule_map = Rules::default();
|
|
for (first, second, product) in rules {
|
|
rule_map[(first - b'A') as usize][(second - b'A') as usize] = product - b'A';
|
|
}
|
|
|
|
(
|
|
initial[0] - b'A',
|
|
initial[initial.len() - 1] - b'A',
|
|
pairs,
|
|
rule_map,
|
|
)
|
|
}
|
|
|
|
fn update(pairs: Pairs, rules: &Rules) -> Pairs {
|
|
let mut new_pairs = Pairs::default();
|
|
|
|
pairs.iter().enumerate().for_each(|(first, row)| {
|
|
row.iter().enumerate().for_each(|(second, &count)| {
|
|
let product = rules[first][second] as usize;
|
|
new_pairs[first][product] += count;
|
|
new_pairs[product][second] += count;
|
|
})
|
|
});
|
|
|
|
new_pairs
|
|
}
|
|
|
|
fn parts_common(input: &mut dyn Read, rounds: usize) -> String {
|
|
let (first, last, mut pairs, rules) = read_input(input);
|
|
|
|
(0..rounds).for_each(|_| pairs = update(pairs, &rules));
|
|
|
|
let mut pair_counts = [0; 26];
|
|
pairs.iter().enumerate().for_each(|(first, row)| {
|
|
row.iter().enumerate().for_each(|(second, &count)| {
|
|
pair_counts[first] += count;
|
|
pair_counts[second] += count;
|
|
})
|
|
});
|
|
|
|
pair_counts[first as usize] += 1;
|
|
pair_counts[last as usize] += 1;
|
|
|
|
// Now everything is counted twice, so first half everything
|
|
let counts = pair_counts.map(|pair_count| pair_count / 2);
|
|
|
|
match counts.into_iter().filter(|&c| c != 0).minmax() {
|
|
itertools::MinMaxResult::NoElements => unreachable!(),
|
|
itertools::MinMaxResult::OneElement(_) => 0,
|
|
itertools::MinMaxResult::MinMax(min, max) => max - min,
|
|
}
|
|
.to_string()
|
|
}
|
|
|
|
pub fn part1(input: &mut dyn Read) -> String {
|
|
parts_common(input, 10)
|
|
}
|
|
|
|
pub fn part2(input: &mut dyn Read) -> String {
|
|
parts_common(input, 40)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
use crate::test_implementation;
|
|
|
|
const SAMPLE: &[u8] = include_bytes!("samples/14.txt");
|
|
|
|
#[test]
|
|
fn sample_part1() {
|
|
test_implementation(part1, SAMPLE, 1588);
|
|
}
|
|
|
|
#[test]
|
|
fn sample_part2() {
|
|
test_implementation(part2, SAMPLE, 2188189693529u64);
|
|
}
|
|
}
|