-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7e295bb
commit ffd8da7
Showing
8 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
5 HLJD, 1 QHSZD, 13 SKZX => 8 MQPH | ||
10 LSLV => 4 JNJHW | ||
1 MQGF, 4 ZWXDQ, 1 GNSZ => 9 DGDH | ||
1 SKZX, 3 DJSP => 1 MCHV | ||
6 TWSR, 10 ZHDFS, 10 LQZXQ => 9 LXQNX | ||
1 FRVW, 1 CJTW => 9 BRCB | ||
20 ZHVNP => 8 XMXL | ||
7 JQJXP => 1 ZGZDW | ||
13 KRCM => 6 KXPQ | ||
4 ZWXDQ, 4 KFKQF, 1 DZDX => 2 MQGF | ||
8 DZDX, 2 ZKGM => 3 KFKQF | ||
3 FXFTB => 8 KVDGP | ||
10 MVGLF, 3 MWFBW, 13 XMXL, 1 CJTW, 2 ZSXJZ, 2 TNCZH, 3 MPFKN, 6 LXQNX => 2 MZMZQ | ||
5 FRVW => 3 NWBTP | ||
1 MVGLF, 2 NLXD, 6 KVDGP, 2 MQPH, 4 FXTJ, 10 TKXKF, 2 FRWV => 2 CSNS | ||
13 TWSR => 9 BNWT | ||
2 KRCM => 7 LSLV | ||
1 ZHDFS, 11 NTVZD, 1 JQJXP => 6 ZHVNP | ||
2 MCHV, 1 JNJHW => 6 NDQNH | ||
32 SMHJH, 6 KXPQ => 1 CJTW | ||
15 FXFTB, 1 MVGLF => 9 MPFKN | ||
119 ORE => 9 KRCM | ||
3 TNCZH => 9 BFQLT | ||
5 MPFKN, 7 TKXKF, 6 JQJXP, 2 DZDX, 16 LCQJ, 4 DGDH, 4 ZGZDW => 7 WVXW | ||
1 ZHDFS, 1 LXQNX => 3 TNCZH | ||
4 ZMVKM, 1 BRQT => 3 QHSZD | ||
24 FRVW, 1 KVDGP, 2 ZLNM => 3 FGLNK | ||
2 KXPQ, 1 LSLV, 22 HNRQ => 5 ZWXDQ | ||
6 ZWXDQ => 1 FRVW | ||
1 FXFTB, 2 MWFBW => 6 ZHDFS | ||
32 FRVW => 5 FRWV | ||
6 FXFTB, 6 NDQNH, 2 MWFBW => 1 JQJXP | ||
9 ZMVKM, 6 QHSZD, 5 LSLV => 4 SMHJH | ||
3 CHKZ => 6 HLJD | ||
21 BFQLT => 6 FXTJ | ||
1 SMHJH, 4 FXFTB => 6 CHKZ | ||
13 FRVW, 13 JQJXP, 1 GNSZ => 8 ZSXJZ | ||
2 NDQNH => 8 NTVZD | ||
3 KRCM => 2 ZKGM | ||
13 ZHDFS, 14 ZWXDQ, 1 CHKZ => 7 LQZXQ | ||
2 BNWT, 3 CHKZ => 7 ZLNM | ||
167 ORE => 1 BRQT | ||
1 LSLV => 3 DZDX | ||
8 MZMZQ, 7 NWBTP, 3 WVXW, 44 MQPH, 3 DJSP, 1 CSNS, 3 BRCB, 32 LQZXQ => 1 FUEL | ||
8 ZLNM => 2 NLXD | ||
30 JQJXP, 9 FGLNK => 7 LCQJ | ||
1 ZKGM, 19 KXPQ => 8 DJSP | ||
4 DJSP => 6 FXFTB | ||
25 NFTPZ => 6 ZMVKM | ||
14 ZHVNP, 1 MVGLF => 9 TKXKF | ||
1 BRQT => 2 SKZX | ||
6 ZKGM => 7 HNRQ | ||
3 DZDX => 5 TWSR | ||
1 SMHJH => 7 MVGLF | ||
3 NDQNH => 1 GNSZ | ||
153 ORE => 9 NFTPZ | ||
14 MCHV, 4 JNJHW, 2 DJSP => 4 MWFBW |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
//! # Space Stoichiometry | ||
//! | ||
//! Sorting the reactions in [topological order](https://en.wikipedia.org/wiki/Topological_sorting) | ||
//! from `FUEL` at the start to `ORE` at the end, allows us to process each reaction only once. | ||
use crate::util::hash::*; | ||
use crate::util::iter::*; | ||
use crate::util::parse::*; | ||
use std::cmp::Ordering; | ||
|
||
struct Ingredient { | ||
amount: u64, | ||
chemical: usize, | ||
} | ||
|
||
pub struct Reaction { | ||
amount: u64, | ||
chemical: usize, | ||
ingredients: Vec<Ingredient>, | ||
} | ||
|
||
/// To speed things up when processing, we use a temporary map to convert chemical names into | ||
/// contiguous indices. | ||
pub fn parse(input: &str) -> Vec<Reaction> { | ||
let lines: Vec<_> = input.lines().collect(); | ||
|
||
let mut reactions: Vec<_> = (0..lines.len() + 1) | ||
.map(|_| { | ||
Reaction { | ||
amount: 0, | ||
chemical: 1, // Default to ORE, other chemicals will overwrite. | ||
ingredients: Vec::new(), | ||
} | ||
}) | ||
.collect(); | ||
|
||
// Assign FUEL and ORE known indices as we'll need to look them up later. | ||
let mut indices = FastMap::new(); | ||
indices.insert("FUEL", 0); | ||
indices.insert("ORE", 1); | ||
|
||
for line in lines { | ||
let mut tokens = line | ||
.split(|c: char| !c.is_ascii_alphanumeric()) | ||
.filter(|s| !s.is_empty()) | ||
.rev() | ||
.chunk::<2>(); | ||
|
||
// Assigns other indices in the arbitrary order that chemicals are encountered. | ||
let [kind, amount] = tokens.next().unwrap(); | ||
let size = indices.len(); | ||
let chemical = *indices.entry(kind).or_insert(size); | ||
|
||
let reaction = &mut reactions[chemical]; | ||
reaction.amount = amount.unsigned(); | ||
reaction.chemical = chemical; | ||
|
||
for [kind, amount] in tokens { | ||
let amount = amount.unsigned(); | ||
let size = indices.len(); | ||
let chemical = *indices.entry(kind).or_insert(size); | ||
reaction.ingredients.push(Ingredient { amount, chemical }); | ||
} | ||
} | ||
|
||
// Sort reactions in topological order | ||
let mut order = vec![0; reactions.len()]; | ||
topological(&reactions, &mut order, 0, 0); | ||
reactions.sort_unstable_by_key(|r| order[r.chemical]); | ||
reactions | ||
} | ||
|
||
/// Calculate the amount of ore needed for 1 fuel. This will be the most ore needed per unit of | ||
/// fuel. Larger amounts of fuel can use some of the leftover chemicals from intermediate reactions. | ||
pub fn part1(input: &[Reaction]) -> u64 { | ||
ore(input, 1) | ||
} | ||
|
||
/// Find the maximum amount of fuel possible from 1 trillion ore with an efficient binary search. | ||
pub fn part2(input: &[Reaction]) -> u64 { | ||
let threshold = 1_000_000_000_000; | ||
let mut start = 1; | ||
let mut end = threshold; | ||
|
||
while start < end { | ||
let middle = (start + end) / 2; | ||
|
||
match ore(input, middle).cmp(&threshold) { | ||
Ordering::Less => start = middle + 1, | ||
Ordering::Equal => return middle, | ||
Ordering::Greater => end = middle - 1, | ||
} | ||
} | ||
|
||
start | ||
} | ||
|
||
/// Sort reactions in topological order from FUEL at the root to ORE at the leaves. Reactions may | ||
/// occur more than once at different depths in the graph, so we take the maximum depth. | ||
fn topological(reactions: &[Reaction], order: &mut [usize], chemical: usize, depth: usize) { | ||
order[chemical] = order[chemical].max(depth); | ||
|
||
for ingredient in &reactions[chemical].ingredients { | ||
topological(reactions, order, ingredient.chemical, depth + 1); | ||
} | ||
} | ||
|
||
/// Run the reactions to find ore needed. Each chemical is processed only once, so we don't need | ||
/// to track excess values of intermediate chemicals. | ||
fn ore(reactions: &[Reaction], amount: u64) -> u64 { | ||
let mut total = vec![0; reactions.len()]; | ||
total[0] = amount; | ||
|
||
for reaction in &reactions[..reactions.len() - 1] { | ||
let multiplier = div_ceil(total[reaction.chemical], reaction.amount); | ||
|
||
for ingredient in &reaction.ingredients { | ||
total[ingredient.chemical] += multiplier * ingredient.amount; | ||
} | ||
} | ||
|
||
total[1] | ||
} | ||
|
||
/// Stable implementation of the nightly `div_ceil` method. | ||
fn div_ceil(a: u64, b: u64) -> u64 { | ||
if a % b == 0 { | ||
a / b | ||
} else { | ||
(a / b) + 1 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,6 +55,7 @@ mod year2019 { | |
mod day11_test; | ||
mod day12_test; | ||
mod day13_test; | ||
mod day14_test; | ||
} | ||
|
||
mod year2020 { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
use aoc::year2019::day14::*; | ||
|
||
const EXAMPLE: &str = "\ | ||
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"; | ||
|
||
#[test] | ||
fn part1_test() { | ||
let input = parse(EXAMPLE); | ||
assert_eq!(part1(&input), 13312); | ||
} | ||
|
||
#[test] | ||
fn part2_test() { | ||
let input = parse(EXAMPLE); | ||
assert_eq!(part2(&input), 82892753); | ||
} |