mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
Implement 2019 day 14
This commit is contained in:
71
2019/aoc2019/day14.py
Normal file
71
2019/aoc2019/day14.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import math
|
||||||
|
from collections import defaultdict
|
||||||
|
from typing import TextIO, Tuple
|
||||||
|
|
||||||
|
from networkx import DiGraph, topological_sort
|
||||||
|
|
||||||
|
|
||||||
|
def read_pair(item: str) -> Tuple[str, int]:
|
||||||
|
amount, element = item.split(' ')
|
||||||
|
|
||||||
|
return element, int(amount)
|
||||||
|
|
||||||
|
|
||||||
|
def read_recipes(data: TextIO) -> DiGraph:
|
||||||
|
graph = DiGraph()
|
||||||
|
|
||||||
|
for line in data:
|
||||||
|
requisites, production = line.strip().split(' => ')
|
||||||
|
|
||||||
|
produced, produced_amount = read_pair(production)
|
||||||
|
graph.add_node(produced, weight=produced_amount)
|
||||||
|
|
||||||
|
for requisite in requisites.split(', '):
|
||||||
|
required, required_amount = read_pair(requisite)
|
||||||
|
graph.add_edge(produced, required, weight=required_amount)
|
||||||
|
|
||||||
|
return graph
|
||||||
|
|
||||||
|
|
||||||
|
def ore_required(graph: DiGraph, fuel_required: int) -> int:
|
||||||
|
requirements = defaultdict(int)
|
||||||
|
requirements['FUEL'] = fuel_required
|
||||||
|
|
||||||
|
for element in topological_sort(graph):
|
||||||
|
if element not in requirements:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if element == 'ORE':
|
||||||
|
break
|
||||||
|
|
||||||
|
element_produced = graph.nodes[element]['weight']
|
||||||
|
productions_required = math.ceil(requirements[element] / element_produced)
|
||||||
|
|
||||||
|
for _, elem_required, amount_required in graph.edges(element, data='weight'):
|
||||||
|
requirements[elem_required] += amount_required * productions_required
|
||||||
|
|
||||||
|
return requirements['ORE']
|
||||||
|
|
||||||
|
|
||||||
|
def part1(data: TextIO) -> int:
|
||||||
|
return ore_required(read_recipes(data), 1)
|
||||||
|
|
||||||
|
|
||||||
|
def part2(data: TextIO) -> int:
|
||||||
|
ore_available = 1000000000000
|
||||||
|
graph = read_recipes(data)
|
||||||
|
|
||||||
|
min_possible = 1 # lower bound of ORE / ore_required(graph, 1) exists but is slower
|
||||||
|
max_possible = ore_available
|
||||||
|
|
||||||
|
while min_possible != max_possible:
|
||||||
|
check = min_possible + (max_possible - min_possible + 1) // 2
|
||||||
|
|
||||||
|
required = ore_required(graph, check)
|
||||||
|
|
||||||
|
if required <= ore_available:
|
||||||
|
min_possible = check
|
||||||
|
else:
|
||||||
|
max_possible = check - 1
|
||||||
|
|
||||||
|
return min_possible
|
||||||
80
2019/tests/test_day14.py
Normal file
80
2019/tests/test_day14.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
from io import StringIO
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from aoc2019.day14 import part1, part2
|
||||||
|
|
||||||
|
SAMPLES = [
|
||||||
|
dedent("""\
|
||||||
|
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
|
||||||
|
"""),
|
||||||
|
dedent("""\
|
||||||
|
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
|
||||||
|
"""),
|
||||||
|
dedent("""\
|
||||||
|
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
|
||||||
|
"""),
|
||||||
|
dedent("""\
|
||||||
|
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
|
||||||
|
"""),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("sample,correct", zip(SAMPLES, [165, 13312, 180697, 2210736]))
|
||||||
|
def test_part1(sample: str, correct: int):
|
||||||
|
data = StringIO(sample)
|
||||||
|
|
||||||
|
result = part1(data)
|
||||||
|
|
||||||
|
assert result == correct
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("sample,correct", zip(SAMPLES[1:], [82892753, 5586022, 460664]))
|
||||||
|
def test_part2(sample: str, correct: int):
|
||||||
|
data = StringIO(sample)
|
||||||
|
|
||||||
|
result = part2(data)
|
||||||
|
|
||||||
|
assert result == correct
|
||||||
Reference in New Issue
Block a user