-
Notifications
You must be signed in to change notification settings - Fork 0
/
day14.py
107 lines (70 loc) · 3.18 KB
/
day14.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#!/usr/bin/env python3
from collections import defaultdict
from collections import namedtuple
import aoc
SHIP_CARGO_CAPACITY = 1000000000000
class FuelFactory:
Ingredient = namedtuple('Ingredient', ['ingredient', 'amount'])
Recipe = namedtuple('Recipe', ['makes', 'ingredients'])
def __init__(self, input_list):
# Recipes indexed by ingredient name.
self._reaction_table = {} # Recipes indexed by ingredient name.
# Keep track of ingredients we have made and not yet used. Amount available indexed by
# ingredient name.
self._available = defaultdict(int)
# Total ORE used.
self._ore_used = 0
self._parse_input_to_reaction_table(input_list)
def _parse_input_to_reaction_table(self, input_list):
for line in input_list:
raw_materials, produced = line.split("=>")
amount_produced, chemical_produced = produced.split()
amount_produced = int(amount_produced)
assert chemical_produced not in self._reaction_table
ingredients = []
for material in raw_materials.split(","):
material.strip()
quantity, chemical = material.split()
quantity = int(quantity)
ingredients += [self.Ingredient(ingredient=chemical, amount=quantity)]
recipe = self.Recipe(makes=amount_produced, ingredients=ingredients)
self._reaction_table[chemical_produced] = recipe
def get_ore_used(self):
return self._ore_used
def make_fuel(self, amount=1):
return self._make_ingredient("FUEL", amount)
def _make_ingredient(self, ingredient, amount):
if ingredient == "ORE":
self._ore_used += amount
return
while self._available[ingredient] < amount:
# We may have some available so calculate how much more we need.
amount_to_make = amount - self._available[ingredient]
recipe = self._reaction_table[ingredient]
batches_to_make = amount_to_make // recipe.makes
if amount_to_make % recipe.makes != 0:
batches_to_make += 1
for ingredient_info in recipe.ingredients:
# Recursively call to make each ingredient needed.
amount_needed = ingredient_info.amount * batches_to_make
self._make_ingredient(ingredient_info.ingredient, amount_needed)
self._available[ingredient] += recipe.makes * batches_to_make
self._available[ingredient] -= amount
def part1(input_list):
fuel_factory = FuelFactory(input_list)
fuel_factory.make_fuel()
return fuel_factory.get_ore_used()
def part2(input_list):
fuel_factory = FuelFactory(input_list)
fuel_factory.make_fuel(1)
units_made = 1
ore_per_unit = fuel_factory.get_ore_used()
while fuel_factory.get_ore_used() < SHIP_CARGO_CAPACITY:
units_to_make = (SHIP_CARGO_CAPACITY - fuel_factory.get_ore_used()) // ore_per_unit
fuel_factory.make_fuel(units_to_make)
if units_to_make == 0:
break
units_made += units_to_make
return units_made
if __name__ == "__main__":
aoc.main(part1, part2)