diff --git a/day19.py b/day19.py index 2416747..da02c43 100644 --- a/day19.py +++ b/day19.py @@ -11,9 +11,10 @@ class Material(Enum): class Robot: - def __init__(self, produces: Material, costs: dict): + def __init__(self, produces: Material, costs: dict, min_viable: int = 0): self.produces = produces self.costs = costs + self.min_viable = min_viable class Blueprint: @@ -36,7 +37,7 @@ class Inventory: Material.OBSIDIAN: 0, Material.GEODE: 0, } - self.build_queue = [] + self.__build_queue = [] def gather(self) -> None: for mat, amount in self.robots.items(): @@ -45,12 +46,12 @@ class Inventory: def queue_build_robot(self, robot: Robot) -> None: for cost_mat, cost_amount in robot.costs.items(): self.materials[cost_mat] -= cost_amount - self.build_queue.append(robot) + self.__build_queue.append(robot) def build_queued_robots(self) -> None: - for robot in self.build_queue: + for robot in self.__build_queue: self.robots[robot.produces] += 1 - self.build_queue = [] + self.__build_queue = [] def build_robot(self, robot: Robot) -> None: self.queue_build_robot(robot) @@ -63,7 +64,7 @@ class Inventory: return new_inv -def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> list: +def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> (Inventory, list): could_build = [] for mat in Material: for robot_mat, robot_cost in bp.robots[mat].costs.items(): @@ -75,47 +76,44 @@ def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> list: inv.gather() - return could_build + return inv, could_build -def get_quality(bp: Blueprint, inv: Inventory, ignore: list, time_left: int = 24, min_geode: int = 0) -> (int, int): - #print("get_quality", time_left, inv.materials, inv.robots) +def get_quality(bp: Blueprint, inv: Inventory, ignore: list, time_left: int = 24) -> int: for m in range(time_left): - could_build = pass_minute(bp, inv, ignore) - if bp.robots[Material.GEODE] in could_build and time_left - m > min_geode: - min_geode = time_left - m - print("Can build Geode Robot with", min_geode, "min left.") - print(inv.robots, inv.materials) + inv, could_build = pass_minute(bp, inv, ignore) - if could_build and time_left - m > 1 and (inv.robots[Material.GEODE] > 0 or time_left > min_geode): + if could_build and time_left - m > 1: max_quality = 0 + build_a_bot = False for robot in could_build: + if robot.min_viable > time_left - m: + continue sub_inv = inv.copy() sub_inv.build_robot(robot) - sub_quality, sub_min_geode = get_quality(bp, sub_inv, [], time_left - m - 1, min_geode) + sub_quality = get_quality(bp, sub_inv, [], time_left - m - 1) if sub_quality > max_quality: max_quality = sub_quality - if sub_min_geode > min_geode: - min_geode = sub_min_geode + build_a_bot = True - sub_quality, sub_min_geode = get_quality(bp, inv, could_build, time_left - m - 1, min_geode) - if sub_quality > max_quality: - max_quality = sub_quality - if sub_min_geode > min_geode: - min_geode = sub_min_geode + if build_a_bot: + sub_quality = get_quality(bp, inv, could_build, time_left - m - 1) + if sub_quality > max_quality: + max_quality = sub_quality - return max_quality, min_geode + return max_quality - return inv.materials[Material.GEODE], min_geode + return inv.materials[Material.GEODE] class Day(AOCDay): inputs = [ [ (33, "input19_test"), - (None, "input19"), + (1616, "input19"), ], [ + (56*62, "input19_test"), (None, "input19"), ] ] @@ -132,10 +130,10 @@ class Day(AOCDay): geode_ore_cost = int(parts[27]) geode_obsi_cost = int(parts[30]) robots = { - Material.ORE: Robot(Material.ORE, {Material.ORE: ore_ore_cost}), - Material.CLAY: Robot(Material.CLAY, {Material.ORE: clay_ore_cost}), - Material.OBSIDIAN: Robot(Material.OBSIDIAN, {Material.ORE: obsi_ore_cost, Material.CLAY: obsi_clay_cost}), - Material.GEODE: Robot(Material.GEODE, {Material.ORE: geode_ore_cost, Material.OBSIDIAN: geode_obsi_cost}) + Material.ORE: Robot(Material.ORE, {Material.ORE: ore_ore_cost}, 7), + Material.CLAY: Robot(Material.CLAY, {Material.ORE: clay_ore_cost}, 5), + Material.OBSIDIAN: Robot(Material.OBSIDIAN, {Material.ORE: obsi_ore_cost, Material.CLAY: obsi_clay_cost}, 3), + Material.GEODE: Robot(Material.GEODE, {Material.ORE: geode_ore_cost, Material.OBSIDIAN: geode_obsi_cost}, 1) } bp.append(Blueprint(blueprint_id, robots)) @@ -143,15 +141,21 @@ class Day(AOCDay): def part1(self) -> Any: blueprints = self.get_blueprints() - #for b in blueprints: - b = blueprints[0] - print("BP", b.bp_id, "Q", get_quality(b, Inventory(), [], 24)) - b = blueprints[1] - print("BP", b.bp_id, "Q", get_quality(b, Inventory(), [], 24)) - return "" + score = 0 + for b in blueprints: + quality = get_quality(b, Inventory(), []) + print("BP", b.bp_id, "Q", quality) + score += quality * b.bp_id + return score def part2(self) -> Any: - return "" + blueprints = self.get_blueprints() + score = 1 + for b in blueprints[:3]: + quality = get_quality(b, Inventory(), [], 32) + print("BP", b.bp_id, "Q", quality) + score *= quality + return score if __name__ == '__main__':