Skip to content

Instantly share code, notes, and snippets.

@pr0g
Last active December 23, 2019 17:40
Show Gist options
  • Save pr0g/e346b4f34d0a6724a3ca7437db278947 to your computer and use it in GitHub Desktop.
Save pr0g/e346b4f34d0a6724a3ca7437db278947 to your computer and use it in GitHub Desktop.
use std::collections::HashMap;
use std::fs;
#[derive(Debug)]
struct Quantity {
element: String,
count: i64,
}
#[derive(Debug)]
struct Reaction {
input: Vec<Quantity>,
output: Quantity,
}
#[derive(Debug, Default)]
struct OreDeposits {
total: i64,
in_use: i64
}
impl Quantity {
fn new(element: String, count: i64) -> Self {
Quantity { element, count }
}
}
impl Reaction {
fn new(output: Quantity, input: Vec<Quantity>) -> Self {
Reaction { input, output }
}
}
type Buckets = HashMap<String, OreDeposits>;
fn main() {
let contents = fs::read_to_string("reactions.txt").expect("error");
let reactions = contents
.trim()
.split('\n')
.collect::<Vec<_>>()
.iter()
.map(|str| str.trim())
.map(|str| str.split("=>").collect::<Vec<_>>())
.map(|vstr| (vstr[0].split(',').collect::<Vec<_>>(), vstr[1]))
.map(|tup| {
(
tup.0.iter().map(|str| str.trim()).collect::<Vec<_>>(),
tup.1.split(' ').filter(|x| !x.is_empty()).collect::<Vec<_>>(),
)
})
.map(|tup| {
(
tup.0
.iter()
.map(|str| str.split(' ').collect::<Vec<_>>())
.collect::<Vec<_>>(),
tup.1,
)
})
.map(|tup| {
(
tup.0
.iter()
.map(|v| (v[0].parse::<i64>().unwrap(), v[1]))
.collect::<Vec<_>>(),
((tup.1)[0].parse::<i64>().unwrap(), (tup.1)[1]),
)
})
.map(|tup| {
let output = Quantity::new((tup.1).1.to_string(), (tup.1).0);
let input = tup
.0
.iter()
.map(|quantity| Quantity::new(quantity.1.to_string(), quantity.0))
.collect::<Vec<_>>();
Reaction::new(output, input)
})
.collect::<Vec<_>>();
fn ore_required(
reactions: &[Reaction],
element: &str,
buckets: &mut Buckets,
ore: &mut i64,
amount: i64,
) {
// find the reaction needed for the output
if let Some(reaction) = reactions
.iter()
.find(|&x| x.output.element.as_str() == element)
{
for input in &reaction.input {
if let Some(b) = buckets.get_mut(&input.element) {
if let Some(input_reaction) = reactions
.iter()
.find(|&x| x.output.element.as_str() == input.element)
{
let surplus = b.total - b.in_use;
let inputs_needed = input.count * amount - surplus;
let reactions_to_run = ((inputs_needed as f64
/ input_reaction.output.count as f64)
.ceil()) as i64;
let inputs_produced = input_reaction.output.count * reactions_to_run;
b.total += inputs_produced;
b.in_use += inputs_needed + surplus;
if input_reaction.input[0].element.as_str() == "ORE" {
*ore += (reactions_to_run as i64) * input_reaction.input[0].count;
} else {
ore_required(reactions, &input.element, buckets, ore, reactions_to_run);
}
}
}
}
}
}
// part 1
{
let mut buckets = Buckets::new();
for reaction in &reactions {
buckets.insert(reaction.output.element.to_string(), OreDeposits::default());
}
let mut ore = 0;
ore_required(&reactions, "FUEL", &mut buckets, &mut ore, 1);
println!("part 1 - {:?}", ore);
}
// part 2
{
let target = 1_000_000_000_000;
let mut min_fuel = 1000;
let mut max_fuel = 10000000;
let mut fuel = 0;
// <= to return fuel for value smaller than one trillion
while min_fuel <= max_fuel {
// reset buckets
let mut buckets = Buckets::new();
for reaction in &reactions {
buckets.insert(reaction.output.element.to_string(), OreDeposits::default());
}
fuel = min_fuel + (max_fuel - min_fuel) / 2;
let mut ore = 0;
ore_required(&reactions, "FUEL", &mut buckets, &mut ore, fuel);
if fuel == ore {
break;
}
if ore < target {
min_fuel = fuel + 1;
} else {
max_fuel = fuel - 1
}
}
println!("part 2 - {:?}", fuel);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment