import matrix import shared from pprint import pprint from dataclasses import dataclass from scanf import scanf def load_blueprints(rows): blueprints = {} for row in rows: b, o_c, c_c, o_o_c, o_c_c, g_o_c, g_o_c = scanf( "Blueprint %d: Each ore robot costs %d ore. " "Each clay robot costs %d ore. " "Each obsidian robot costs %d ore and %d clay. " "Each geode robot costs %d ore and %d", row) bp = Blueprint(b, o_c, c_c, o_o_c, o_c_c, g_o_c, g_o_c) blueprints[bp.id] = bp return blueprints @dataclass class Blueprint(): id :int ore_ore_cost: int clay_ore_cost: int obsidian_ore_cost: int obsidian_clay_cost: int geode_ore_cost: int geode_obsidian_cost: int def can_produce_ore_robot(self, r): return r.ore >= self.ore_cost def can_produce_clay_robot(self, r): return r.ore >= self.clay_ore_cost def can_produce_obsidian_robot(self, r): return r.ore >= self.obsidian_ore_cost and r.clay >= self.obsidian_clay_cost def can_produce_geode_robot(self, r): return r.ore >= self.geode_ore_cost and r.obsidian >= self.geode_obsidian_cost @dataclass class Resources(): ore: int = 0 clay: int = 0 obsidian: int = 0 geodes: int = 0 @dataclass class Robots(): ore: int = 1 clay: int = 0 obsidian: int = 0 geodes: int = 0 # @shared.profile #def part1(rows, minutes=12): def part1(rows, minutes=24): blueprints = load_blueprints(rows) quality = 0 for _,bp in blueprints.items(): resources = Resources() robots = Robots() for minute in range(1, minutes+1): print(f"== Minute {minute} ==") _add_ore = False _add_clay = False _add_obsidian = False _add_geode = False # SPEND THE RESOURCES if bp.can_produce_clay_robot(resources): # Do we have enough to build obsidian? if robots.clay < bp.obsidian_ore_cost: print(f"Spend {bp.clay_ore_cost} ore to start building a clay-collecting robot.") resources.ore -= bp.clay_ore_cost _add_clay = True if bp.can_produce_obsidian_robot(resources): # Do we have enough to build geodes? if robots.obsidian < bp.geode_obsidian_cost: print(f"Spend {bp.obsidian_ore_cost} ore and {bp.obsidian_clay_cost} clay to start building an obsidian-collecting robot.") resources.ore -= bp.obsidian_ore_cost resources.clay -= bp.obsidian_clay_cost _add_obsidian = True if bp.can_produce_geode_robot(resources): print(f"Spend {bp.geode_ore_cost} ore and {bp.geode_obsidian_cost} obsidian to start building a geode-cracking robot.") resources.ore -= bp.geode_ore_cost resources.obsidian -= bp.geode_obsidian_cost _add_geode = True # GATHER RESOURCES resources.ore += robots.ore resources.clay += robots.clay resources.obsidian += robots.obsidian resources.geodes += robots.geodes if robots.ore > 0: print(f"{robots.ore} ore-collecting robot collects {robots.ore} ore; you now have {resources.ore} ore.") if robots.clay > 0: print(f"{robots.clay} clay-collecting robot collects {robots.clay} clay; you now have {resources.clay} clay.") if robots.obsidian > 0: print(f"{robots.obsidian} obsidian-collecting robot collects {robots.obsidian} obsidian; you now have {resources.obsidian} obsidian.") if robots.geodes > 0: print(f"{robots.geodes} geode-cracking robot collects {robots.geodes} geodes; you now have {resources.geodes} geodes.") # PRODUCE ROBOTS if _add_ore: robots.ore += 1 print(f"The new ore-collecting robot is ready; you now have {robots.ore} of them.") if _add_clay: robots.clay += 1 print(f"The new clay-collecting robot is ready; you now have {robots.clay} of them.") if _add_obsidian: robots.obsidian += 1 print(f"The new obsidian-collecting robot is ready; you now have {robots.obsidian} of them.") if _add_geode: robots.geodes += 1 print(f"The new geode-cracking robot is ready; you now have {robots.geodes} of them.") print() quality += (bp.id * resources.geodes) print(quality) # @shared.profile def part2(rows, minutes=24): pass def main(): rows = [row for row in shared.load_rows(19)] with shared.elapsed_timer() as elapsed: part1(rows) print("🕒", elapsed()) with shared.elapsed_timer() as elapsed: part2(rows) print("🕒", elapsed()) if __name__ == "__main__": main()