mirror of
https://github.com/bertptrs/adventofcode.git
synced 2025-12-25 21:00:31 +01:00
72 lines
1.9 KiB
Python
72 lines
1.9 KiB
Python
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
|