This commit is contained in:
foosinn 2020-05-26 17:25:50 +02:00
parent e27c10d3b0
commit e16cafaa52
3 changed files with 447 additions and 0 deletions

383
src/day14/main.rs Normal file
View file

@ -0,0 +1,383 @@
use std::cmp::Ordering;
use std::collections::HashMap;
use std::convert::*;
use std::fs::File;
use std::io::{BufRead, BufReader};
type Resource = String;
type Pipeline = HashMap<Resource, ResourceBuilder>;
#[derive(Debug)]
struct ResourceBuilder {
requirements: Vec<Resource>,
out_count: usize,
}
impl From<(Vec<Resource>, usize)> for ResourceBuilder {
fn from(build: (Vec<Resource>, usize)) -> Self {
ResourceBuilder {
requirements: build.0,
out_count: build.1,
}
}
}
#[derive(Default, Debug)]
struct NanoFactory {
pipeline: Pipeline,
ordered: Vec<Resource>,
}
#[derive(Debug)]
enum NanoFactoryBuildState {
InputCount,
InputResource,
OutputArrow,
OutputCount,
OutputResource,
}
impl NanoFactory {
/// sort resources
fn resolve_order(&mut self) {
self.ordered = vec![Resource::from("ORE")];
let mut sortable: HashMap<Resource, Vec<Resource>> = self
.pipeline
.iter()
.map(|(k, v)| (k.clone(), v.requirements.clone()))
.collect();
while sortable.len() > 0 {
sortable = sortable
.into_iter()
.filter_map(|(k, v)| {
if v.iter().all(|i| self.ordered.contains(i)) {
self.ordered.push(k);
None
} else {
Some((k, v))
}
})
.collect();
}
self.ordered.reverse();
self.ordered.pop();
}
/// calculate how much ores are required for required_fuel
fn resource_usage(&self, required_fuel: u128) -> usize {
// create storage to track leftover resources
let mut storage: HashMap<Resource, u128> = HashMap::new();
storage.insert(Resource::from("FUEL"), required_fuel);
// calculate resource costs
self.ordered.iter().for_each(|res| {
let rb = self.pipeline.get(res).unwrap();
let count = (storage.get(res).unwrap().clone() + (rb.out_count as u128) - 1)
/ (rb.out_count as u128);
rb.requirements.iter().for_each(|req| {
storage
.entry(req.clone())
.and_modify(|v| *v += count)
.or_insert(count);
storage.insert(res.clone(), 0);
})
});
// return cost for ore
*storage.get("ORE").unwrap() as usize
}
/// generate a single fuel
fn generate_fuel(&self) -> usize {
let mut storage: HashMap<Resource, usize> = HashMap::new();
self.lookup_resource(&mut storage, "FUEL")
}
/// recursively calculate the cost of a resource in ores
fn lookup_resource(&self, storage: &mut HashMap<Resource, usize>, resource: &str) -> usize {
if resource == "ORE" {
return 1;
}
// use from storage
if let Some(count) = storage.get_mut(resource) {
if *count > 0 {
*count -= 1;
return 0;
}
}
// initialize storage with zero
if storage.get(resource).is_none() {
storage.insert(resource.into(), 0);
}
// build resource and and to storage
let required = self
.pipeline
.get(resource)
.expect("unable to find resource");
let cost = required.requirements.iter().fold(0, |mut acc, c| {
acc += self.lookup_resource(storage, c);
acc
});
// update storage
let stored = storage.get_mut(resource).unwrap();
*stored += required.out_count - 1;
cost
}
}
impl<T> From<T> for NanoFactory
where
T: BufRead,
{
fn from(bufreader: T) -> Self {
let mut f = NanoFactory::default();
let mut state = NanoFactoryBuildState::InputCount;
bufreader.split(b'\n').enumerate().for_each(|(nr, line)| {
let line = line.unwrap();
let mut line = line.into_iter().peekable();
let mut count_var = 0;
let mut cur_requirements = Vec::new();
loop {
match state {
// numeric input
NanoFactoryBuildState::InputCount | NanoFactoryBuildState::OutputCount => {
while let Some(c) = next_if_in_range(&mut line, 48..=57) {
count_var = count_var * 10 + (c as usize) - 48;
}
assert_eq!(line.next().unwrap(), b' ');
state = match state {
NanoFactoryBuildState::InputCount => {
NanoFactoryBuildState::InputResource
}
NanoFactoryBuildState::OutputCount => {
NanoFactoryBuildState::OutputResource
}
_ => unreachable!(),
}
}
// input resouce name
NanoFactoryBuildState::InputResource => {
let mut r = Resource::new();
while let Some(c) = next_if_in_range(&mut line, 65..=90) {
r.push(c.into());
}
for _ in 0..count_var {
cur_requirements.push(r.clone());
}
r.truncate(0);
count_var = 0;
let next = line.next();
match next {
Some(b',') => {
assert_eq!(line.next().unwrap(), b' ');
state = NanoFactoryBuildState::InputCount;
}
Some(b' ') => state = NanoFactoryBuildState::OutputArrow,
_ => panic!(
"invalid format in line {}, expected ' ' or ',', got '{:?}'",
nr, next
),
}
}
// output resource name
NanoFactoryBuildState::OutputResource => {
let mut r = Resource::new();
while let Some(c) = next_if_in_range(&mut line, 65..=90) {
r.push(c.into());
}
f.pipeline
.insert(r, ResourceBuilder::from((cur_requirements, count_var)));
state = NanoFactoryBuildState::InputCount;
return;
}
// arrow between input and output ' => '
NanoFactoryBuildState::OutputArrow => {
assert_eq!(line.next().unwrap(), b'=');
assert_eq!(line.next().unwrap(), b'>');
assert_eq!(line.next().unwrap(), b' ');
state = NanoFactoryBuildState::OutputCount
}
};
}
});
f
}
}
fn next_if_in_range<I, T>(
peekable: &mut std::iter::Peekable<I>,
range: std::ops::RangeInclusive<T>,
) -> Option<T>
where
I: std::iter::Iterator<Item = T>,
T: Copy + std::cmp::PartialOrd,
{
if let Some(peeked) = peekable.peek() {
if range.contains(peeked) {
peekable.next()
} else {
None
}
} else {
None
}
}
fn main() {
let f = File::open("input14").unwrap();
let bufreader = BufReader::new(f);
let mut factory = NanoFactory::from(bufreader);
// part1
println!("ores: {}", factory.generate_fuel());
// part2 as binary search
factory.resolve_order();
let mut min = 1;
let mut max = 1_000_000_000_000;
let mut fuel;
loop {
fuel = (min + max) / 2;
let ore = factory.resource_usage(fuel);
match 1_000_000_000_000.cmp(&ore) {
Ordering::Less => max = fuel - 1,
Ordering::Greater => {
min = fuel + 1;
if min > max {
break;
}
}
Ordering::Equal => {
break;
}
};
}
println!("ammount: {}", fuel);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example1() {
let bufreader = BufReader::new(
"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
"
.trim()
.as_bytes(),
);
let factory = NanoFactory::from(bufreader);
assert_eq!(factory.generate_fuel(), 31);
assert_eq!(factory.resource_usage(1), 31);
}
#[test]
fn example2() {
let bufreader = BufReader::new(
"
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
"
.trim()
.as_bytes(),
);
let factory = NanoFactory::from(bufreader);
assert_eq!(factory.generate_fuel(), 165);
assert_eq!(factory.resource_usage(1), 165);
}
#[test]
fn example3() {
let bufreader = BufReader::new(
"
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
"
.trim()
.as_bytes(),
);
let factory = NanoFactory::from(bufreader);
assert_eq!(factory.generate_fuel(), 13312);
assert_eq!(factory.generate_fuel_from_ores(1_000_000_000_000), 82892753);
}
#[test]
fn example4() {
let bufreader = BufReader::new(
"
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
"
.trim()
.as_bytes(),
);
let factory = NanoFactory::from(bufreader);
assert_eq!(factory.generate_fuel(), 180697);
assert_eq!(factory.resource_usage(1), 180697);
// TODO assert_eq!(factory.generate_fuel_from_ores(1_000_000_000_000), 5586022);
}
#[test]
fn example5() {
let bufreader = BufReader::new(
"
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
"
.trim()
.as_bytes(),
);
let factory = NanoFactory::from(bufreader);
assert_eq!(factory.generate_fuel(), 2210736);
assert_eq!(factory.resource_usage(1), 2210736);
// TODO assert_eq!(factory.generate_fuel_from_ores(1_000_000_000_000), 460664);
}
}