from math import ceil from tools.aoc import AOCDay from typing import Any class Day(AOCDay): test_solutions_p1 = [165, 13312, 180697, 2210736] test_solutions_p2 = [82892753, 5586022, 460664] conversions = {} rest = {} def buildConversionDict(self): self.rest = {} conv_input = self.getInputAsArraySplit(" => ") for need_str, give_str in conv_input: tar_amt, tar = give_str.split() self.conversions[tar] = {'amount': int(tar_amt), 'need': {}} for r in need_str.split(", "): a, n = r.split() self.conversions[tar]['need'][n] = int(a) def getOreNeed(self, what: str, amount: int) -> int: amount -= self.rest.get(what, 0) if amount < 0: self.rest[what] = abs(amount) return 0 if what == 'ORE': return amount ore_need = 0 count = ceil(amount / self.conversions[what]['amount']) for need, need_amt in self.conversions[what]['need'].items(): ore_need += self.getOreNeed(need, need_amt * count) self.rest[what] = self.conversions[what]['amount'] * count - amount return ore_need def part1(self) -> Any: self.buildConversionDict() return self.getOreNeed('FUEL', 1) def part2(self) -> Any: self.buildConversionDict() got_ore = 1_000_000_000_000 min_fuel = got_ore // self.getOreNeed('FUEL', 1) factor = 100_000 while factor >= 1: ore_need = self.getOreNeed('FUEL', min_fuel + factor) if ore_need > got_ore: factor //= 10 else: min_fuel += factor return min_fuel if self.getOreNeed('FUEL', min_fuel) < got_ore else min_fuel - 1