2019 day 14 cleanup: introduced a proper data type for amount-chemical pairs; removed debug instructions; inserted the correct result in the challenge test

This commit is contained in:
Chris Alge 2023-03-13 17:25:10 +01:00
parent ddff0d564d
commit 9c8f23161a

View file

@ -2,9 +2,14 @@ use std::collections::{HashMap, VecDeque};
type Chemical = usize;
struct Reagent {
id: usize,
amount: usize,
}
struct Reaction {
input: Vec<(usize, Chemical)>,
output: (usize, Chemical),
input: Vec<Reagent>,
output: Reagent,
}
impl Reaction {
@ -22,9 +27,12 @@ impl Reaction {
assert_eq!(in_components.len()%3, 0);
let out_components: Vec<_> = out_str.split(' ').collect();
assert_eq!(out_components.len(), 2);
let output = (out_components[0].parse().unwrap(), get_chemical(out_components[1]));
let output = Reagent {
id: get_chemical(out_components[1]),
amount: out_components[0].parse().unwrap(),
};
let input = in_components.chunks(3).map(|c| (c[0].parse::<usize>().unwrap(), get_chemical(c[1]))).collect();
let input = in_components.chunks(3).map(|c| Reagent { id: get_chemical(c[1]), amount: c[0].parse::<usize>().unwrap(), }).collect();
Self {
input,
@ -40,7 +48,7 @@ pub fn run(input: &str) -> (usize, usize) {
let ore = chemicals.iter().position(|chem| chem == &String::from("ORE")).unwrap();
// dbg!(&chemicals);
let first = break_down(&reactions, fuel, ore, 1);
let second = bisection_find(1000000000000/first, 10000000000000/first, &reactions, fuel, ore, 1000000000000);
let second = bisection_find(1_000_000_000_000/first, 10_000_000_000_000/first, &reactions, fuel, ore, 1_000_000_000_000);
(first, second)
}
@ -58,39 +66,35 @@ fn bisection_find(lower: usize, upper: usize, reactions: &[Reaction], target: us
}
fn break_down(reactions: &[Reaction], target: Chemical, raw: Chemical, amount: usize) -> usize {
let mut current = VecDeque::from([(amount, target)]);
let mut current = VecDeque::from([Reagent {amount, id: target, }]);
let mut leftovers = HashMap::new();
while !(current.len() == 1 && current[0].1 == raw) {
let (next_count, next_chem): (usize, Chemical) = current.pop_front().unwrap();
if next_chem == raw {
current.push_back((next_count, next_chem));
while !(current.len() == 1 && current[0].id == raw) {
let next = current.pop_front().unwrap();
if next.id == raw {
current.push_back(next);
continue;
}
// dbg!(next_chem);
let reaction = reactions.iter().find(|r| r.output.1 == next_chem).unwrap();
let multiplier = (next_count + reaction.output.0 - 1)/reaction.output.0;
*leftovers.entry(next_chem).or_insert(0) += (reaction.output.0 * multiplier).saturating_sub(next_count);
// eprintln!("Breaking down {next_count} {next_chem} into");
for (input_count, input_chem) in &reaction.input {
let mut required = input_count * multiplier;
// eprintln!(" {required} {input_chem}");
if let Some(left) = leftovers.get_mut(input_chem) {
let reaction = reactions.iter().find(|r| r.output.id == next.id).unwrap();
let multiplier = (next.amount + reaction.output.amount - 1)/reaction.output.amount;
*leftovers.entry(next.id).or_insert(0) += (reaction.output.amount * multiplier).saturating_sub(next.amount);
for input in &reaction.input {
let mut required = input.amount * multiplier;
if let Some(left) = leftovers.get_mut(&input.id) {
let consumed = required.min(*left);
required -= consumed;
*left -= consumed;
// eprintln!(" {required} after consuming leftovers. {left} left");
}
if required > 0 {
if let Some(idx) = current.iter().position(|c| c.1 == *input_chem) {
current[idx].0 += required;
if let Some(idx) = current.iter().position(|c| c.id == input.id) {
current[idx].amount += required;
} else {
current.push_back((required, *input_chem));
current.push_back(Reagent { id: input.id, amount: required, });
}
}
}
}
current[0].0
current[0].amount
}
#[cfg(test)]
@ -111,6 +115,6 @@ mod tests {
#[test]
fn test_challenge() {
let challenge_input = read_file("tests/challenge_input");
assert_eq!(run(&challenge_input), (1582325, 0));
assert_eq!(run(&challenge_input), (1582325, 2267486));
}
}