mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 12:50:32 +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