Added Solution for 2019 day 14
This commit is contained in:
parent
72914e6246
commit
ddff0d564d
5 changed files with 325 additions and 0 deletions
8
2019/day14_space_stoichiometry/Cargo.toml
Normal file
8
2019/day14_space_stoichiometry/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "day14_space_stoichiometry"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
127
2019/day14_space_stoichiometry/challenge.txt
Normal file
127
2019/day14_space_stoichiometry/challenge.txt
Normal file
|
@ -0,0 +1,127 @@
|
|||
As you approach the rings of Saturn, your ship's *low fuel* indicator turns on. There isn't any fuel here, but the rings have plenty of raw material. Perhaps your ship's Inter-Stellar Refinery Union brand *nanofactory* can turn these raw materials into fuel.
|
||||
|
||||
You ask the nanofactory to produce a list of the *reactions* it can perform that are relevant to this process (your puzzle input). Every reaction turns some quantities of specific *input chemicals* into some quantity of an *output chemical*. Almost every *chemical* is produced by exactly one reaction; the only exception, `ORE`, is the raw material input to the entire process and is not produced by a reaction.
|
||||
|
||||
You just need to know how much `*ORE*` you'll need to collect before you can produce one unit of `*FUEL*`.
|
||||
|
||||
Each reaction gives specific quantities for its inputs and output; reactions cannot be partially run, so only whole integer multiples of these quantities can be used. (It's okay to have leftover chemicals when you're done, though.) For example, the reaction `1 A, 2 B, 3 C => 2 D` means that exactly 2 units of chemical `D` can be produced by consuming exactly 1 `A`, 2 `B` and 3 `C`. You can run the full reaction as many times as necessary; for example, you could produce 10 `D` by consuming 5 `A`, 10 `B`, and 15 `C`.
|
||||
|
||||
Suppose your nanofactory produces the following list of reactions:
|
||||
|
||||
```
|
||||
10 ORE => 10 A
|
||||
1 ORE => 1 B
|
||||
7 A, 1 B => 1 C
|
||||
7 A, 1 C => 1 D
|
||||
7 A, 1 D => 1 E
|
||||
7 A, 1 E => 1 FUEL
|
||||
|
||||
```
|
||||
|
||||
The first two reactions use only `ORE` as inputs; they indicate that you can produce as much of chemical `A` as you want (in increments of 10 units, each 10 costing 10 `ORE`) and as much of chemical `B` as you want (each costing 1 `ORE`). To produce 1 `FUEL`, a total of *31* `ORE` is required: 1 `ORE` to produce 1 `B`, then 30 more `ORE` to produce the 7 + 7 + 7 + 7 = 28 `A` (with 2 extra `A` wasted) required in the reactions to convert the `B` into `C`, `C` into `D`, `D` into `E`, and finally `E` into `FUEL`. (30 `A` is produced because its reaction requires that it is created in increments of 10.)
|
||||
|
||||
Or, suppose you have the following list of reactions:
|
||||
|
||||
```
|
||||
9 ORE => 2 A
|
||||
8 ORE => 3 B
|
||||
7 ORE => 5 C
|
||||
3 A, 4 B => 1 AB
|
||||
5 B, 7 C => 1 BC
|
||||
4 C, 1 A => 1 CA
|
||||
2 AB, 3 BC, 4 CA => 1 FUEL
|
||||
|
||||
```
|
||||
|
||||
The above list of reactions requires *165* `ORE` to produce 1 `FUEL`:
|
||||
|
||||
* Consume 45 `ORE` to produce 10 `A`.
|
||||
* Consume 64 `ORE` to produce 24 `B`.
|
||||
* Consume 56 `ORE` to produce 40 `C`.
|
||||
* Consume 6 `A`, 8 `B` to produce 2 `AB`.
|
||||
* Consume 15 `B`, 21 `C` to produce 3 `BC`.
|
||||
* Consume 16 `C`, 4 `A` to produce 4 `CA`.
|
||||
* Consume 2 `AB`, 3 `BC`, 4 `CA` to produce 1 `FUEL`.
|
||||
|
||||
Here are some larger examples:
|
||||
|
||||
* *13312* `ORE` for 1 `FUEL`:
|
||||
|
||||
```
|
||||
157 ORE => 5 NZVS
|
||||
165 ORE => 6 DCFZ
|
||||
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
|
||||
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
|
||||
179 ORE => 7 PSHF
|
||||
177 ORE => 5 HKGWZ
|
||||
7 DCFZ, 7 PSHF => 2 XJWVT
|
||||
165 ORE => 2 GPVTF
|
||||
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT
|
||||
|
||||
```
|
||||
|
||||
* *180697* `ORE` for 1 `FUEL`:
|
||||
|
||||
```
|
||||
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
|
||||
17 NVRVD, 3 JNWZP => 8 VPVL
|
||||
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
|
||||
22 VJHF, 37 MNCFX => 5 FWMGM
|
||||
139 ORE => 4 NVRVD
|
||||
144 ORE => 7 JNWZP
|
||||
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
|
||||
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
|
||||
145 ORE => 6 MNCFX
|
||||
1 NVRVD => 8 CXFTF
|
||||
1 VJHF, 6 MNCFX => 4 RFSQX
|
||||
176 ORE => 6 VJHF
|
||||
|
||||
```
|
||||
|
||||
* *2210736* `ORE` for 1 `FUEL`:
|
||||
|
||||
```
|
||||
171 ORE => 8 CNZTR
|
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
|
||||
114 ORE => 4 BHXH
|
||||
14 VRPVC => 6 BMBT
|
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
|
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
|
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
|
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
|
||||
5 BMBT => 4 WPTQ
|
||||
189 ORE => 9 KTJDG
|
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
|
||||
12 VRPVC, 27 CNZTR => 2 XDBXC
|
||||
15 KTJDG, 12 BHXH => 5 XCVML
|
||||
3 BHXH, 2 VRPVC => 7 MZWV
|
||||
121 ORE => 7 VRPVC
|
||||
7 XCVML => 6 RJRHP
|
||||
5 BHXH, 4 VRPVC => 5 LTCX
|
||||
|
||||
```
|
||||
|
||||
Given the list of reactions in your puzzle input, *what is the minimum amount of `ORE` required to produce exactly 1 `FUEL`?*
|
||||
|
||||
Your puzzle answer was `1582325`.
|
||||
|
||||
\--- Part Two ---
|
||||
----------
|
||||
|
||||
After collecting `ORE` for a while, you check your cargo hold: *1 trillion* (*1000000000000*) units of `ORE`.
|
||||
|
||||
*With that much ore*, given the examples above:
|
||||
|
||||
* The 13312 `ORE`-per-`FUEL` example could produce *82892753* `FUEL`.
|
||||
* The 180697 `ORE`-per-`FUEL` example could produce *5586022* `FUEL`.
|
||||
* The 2210736 `ORE`-per-`FUEL` example could produce *460664* `FUEL`.
|
||||
|
||||
Given 1 trillion `ORE`, *what is the maximum amount of `FUEL` you can produce?*
|
||||
|
||||
Your puzzle answer was `2267486`.
|
||||
|
||||
Both parts of this puzzle are complete! They provide two gold stars: \*\*
|
||||
|
||||
At this point, you should [return to your Advent calendar](/2019) and try another puzzle.
|
||||
|
||||
If you still want to see it, you can [get your puzzle input](14/input).
|
116
2019/day14_space_stoichiometry/src/lib.rs
Normal file
116
2019/day14_space_stoichiometry/src/lib.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use std::collections::{HashMap, VecDeque};
|
||||
|
||||
type Chemical = usize;
|
||||
|
||||
struct Reaction {
|
||||
input: Vec<(usize, Chemical)>,
|
||||
output: (usize, Chemical),
|
||||
}
|
||||
|
||||
impl Reaction {
|
||||
fn from(line: &str, chemicals: &mut Vec<String>) -> Self {
|
||||
let mut get_chemical = |name: &str| -> usize {
|
||||
if let Some(idx) = chemicals.iter().position(|c| c == &name.to_string()) {
|
||||
idx
|
||||
} else {
|
||||
chemicals.push(name.to_string());
|
||||
chemicals.len()-1
|
||||
}
|
||||
};
|
||||
let (in_str, out_str) = line.split_once(" => ").unwrap();
|
||||
let in_components: Vec<_> = in_str.split(&[' ', ',']).chain([""].into_iter()).collect();
|
||||
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 input = in_components.chunks(3).map(|c| (c[0].parse::<usize>().unwrap(), get_chemical(c[1]))).collect();
|
||||
|
||||
Self {
|
||||
input,
|
||||
output,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(input: &str) -> (usize, usize) {
|
||||
let mut chemicals = Vec::new();
|
||||
let reactions: Vec<_> = input.lines().map(|line| Reaction::from(line, &mut chemicals)).collect();
|
||||
let fuel = chemicals.iter().position(|chem| chem == &String::from("FUEL")).unwrap();
|
||||
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);
|
||||
(first, second)
|
||||
}
|
||||
|
||||
fn bisection_find(lower: usize, upper: usize, reactions: &[Reaction], target: usize, raw: usize, stock: usize) -> usize {
|
||||
if upper-lower < 2 {
|
||||
lower
|
||||
} else {
|
||||
let mid = (upper+lower)/2;
|
||||
if break_down(reactions, target, raw, mid) > stock {
|
||||
bisection_find(lower, mid, reactions, target, raw, stock)
|
||||
} else {
|
||||
bisection_find(mid, upper, reactions, target, raw, stock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn break_down(reactions: &[Reaction], target: Chemical, raw: Chemical, amount: usize) -> usize {
|
||||
let mut current = VecDeque::from([(amount, 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));
|
||||
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 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;
|
||||
} else {
|
||||
current.push_back((required, *input_chem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
current[0].0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn read_file(name: &str) -> String {
|
||||
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let sample_input = read_file("tests/sample_input");
|
||||
assert_eq!(run(&sample_input), (2210736, 460664));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
let challenge_input = read_file("tests/challenge_input");
|
||||
assert_eq!(run(&challenge_input), (1582325, 0));
|
||||
}
|
||||
}
|
57
2019/day14_space_stoichiometry/tests/challenge_input
Normal file
57
2019/day14_space_stoichiometry/tests/challenge_input
Normal file
|
@ -0,0 +1,57 @@
|
|||
1 ZQVND => 2 MBZM
|
||||
2 KZCVX, 1 SZBQ => 7 HQFB
|
||||
1 PFSQF => 9 RSVN
|
||||
2 PJXQB => 4 FSNZ
|
||||
20 JVDKQ, 2 LSQFK, 8 SDNCK, 1 MQJNV, 13 LBTV, 3 KPBRX => 5 QBPC
|
||||
131 ORE => 8 WDQSL
|
||||
19 BRGJH, 2 KNVN, 3 CRKW => 9 MQJNV
|
||||
16 DNPM, 1 VTVBF, 11 JSGM => 1 BWVJ
|
||||
3 KNVN, 1 JQRML => 7 HGQJ
|
||||
1 MRQJ, 2 HQFB, 1 MQJNV => 5 VQLP
|
||||
1 PLGH => 5 DMGF
|
||||
12 DMGF, 3 DNPM, 1 CRKW => 1 CLML
|
||||
1 JSGM, 1 RSVN => 5 TMNKH
|
||||
1 RFJLG, 3 CFWC => 2 ZJMC
|
||||
1 BRGJH => 5 KPBRX
|
||||
1 SZBQ, 17 GBVJF => 4 ZHGL
|
||||
2 PLGH => 5 CFWC
|
||||
4 FCBZS, 2 XQWHB => 8 JSGM
|
||||
2 PFSQF => 2 KNVN
|
||||
12 CRKW, 9 GBVJF => 1 KRCB
|
||||
1 ZHGL => 8 PJMFP
|
||||
198 ORE => 2 XQWHB
|
||||
2 BWVJ, 7 CFWC, 17 DPMWN => 3 KZCVX
|
||||
4 WXBF => 6 JVDKQ
|
||||
2 SWMTK, 1 JQRML => 7 QXGZ
|
||||
1 JSGM, 1 LFSFJ => 4 LSQFK
|
||||
73 KNVN, 65 VQLP, 12 QBPC, 4 XGTL, 10 SWMTK, 51 ZJMC, 4 JMCPR, 1 VNHT => 1 FUEL
|
||||
1 BWVJ, 7 MBZM => 5 JXZT
|
||||
10 CFWC => 2 DPMWN
|
||||
13 LQDLN => 3 LBTV
|
||||
1 PFZW, 3 LQDLN => 5 PJXQB
|
||||
2 RSVN, 2 PFSQF => 5 CRKW
|
||||
1 HGQJ, 3 SMNGJ, 36 JXZT, 10 FHKG, 3 KPBRX, 2 CLML => 3 JMCPR
|
||||
126 ORE => 4 FCBZS
|
||||
1 DNPM, 13 MBZM => 5 PLGH
|
||||
2 XQWHB, 10 FCBZS => 9 LFSFJ
|
||||
1 DPMWN => 9 PFZW
|
||||
1 ZJMC, 3 TMNKH => 2 SWMTK
|
||||
7 TZCK, 1 XQWHB => 5 ZQVND
|
||||
4 CFWC, 1 ZLWN, 5 RSVN => 2 WXBF
|
||||
1 BRGJH, 2 CLML => 6 LQDLN
|
||||
26 BWVJ => 2 GBVJF
|
||||
16 PJXQB, 20 SDNCK, 3 HQFB, 7 QXGZ, 2 KNVN, 9 KZCVX => 8 XGTL
|
||||
8 PJMFP, 3 BRGJH, 19 MRQJ => 5 SMNGJ
|
||||
7 DNPM => 2 SZBQ
|
||||
2 JQRML, 14 SDNCK => 8 FHKG
|
||||
1 FSNZ, 6 RFJLG, 2 CRKW => 8 SDNCK
|
||||
2 CLML, 4 SWMTK, 16 KNVN => 4 JQRML
|
||||
8 TZCK, 18 WDQSL => 2 PFSQF
|
||||
1 LSQFK => 8 VTVBF
|
||||
18 BRGJH, 8 ZHGL, 2 KRCB => 7 VNHT
|
||||
3 TZCK => 4 DNPM
|
||||
14 PFZW, 1 PFSQF => 7 BRGJH
|
||||
21 PLGH, 6 VTVBF, 2 RSVN => 1 ZLWN
|
||||
149 ORE => 2 TZCK
|
||||
3 JSGM => 1 RFJLG
|
||||
4 PFSQF, 4 DMGF => 3 MRQJ
|
17
2019/day14_space_stoichiometry/tests/sample_input
Normal file
17
2019/day14_space_stoichiometry/tests/sample_input
Normal file
|
@ -0,0 +1,17 @@
|
|||
171 ORE => 8 CNZTR
|
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
|
||||
114 ORE => 4 BHXH
|
||||
14 VRPVC => 6 BMBT
|
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
|
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
|
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
|
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
|
||||
5 BMBT => 4 WPTQ
|
||||
189 ORE => 9 KTJDG
|
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
|
||||
12 VRPVC, 27 CNZTR => 2 XDBXC
|
||||
15 KTJDG, 12 BHXH => 5 XCVML
|
||||
3 BHXH, 2 VRPVC => 7 MZWV
|
||||
121 ORE => 7 VRPVC
|
||||
7 XCVML => 6 RJRHP
|
||||
5 BHXH, 4 VRPVC => 5 LTCX
|
Loading…
Add table
Add a link
Reference in a new issue