from queue import PriorityQueue lines = [line.split(".") for line in open("19.input")] blueprints = [list(map(int,[line[0].split(" ")[6], line[1].split(" ")[5], line[2].split(" ")[5], line[2].split(" ")[8], line[3].split(" ")[5], line[3].split(" ")[8]])) for line in lines] def add(a, b): return tuple(a_ + b_ for (a_, b_) in zip(list(a), list(b))) def sub(a, b): return tuple(a_ - b_ for (a_, b_) in zip(list(a), list(b))) def lte(a, b): return all(a_ <= b_ for (a_, b_) in zip(list(a), list(b))) def possible_purchases(res, robo, rem, costs, max_costs): """Returns set of possible purchase tuples""" result = set() for robo_idx, cost in enumerate(costs): if lte(cost, res): result.add(robo_idx) if 3 in result: return {3} for i in range(3): if res[i] + robo[i] * rem >= max_costs[i] * rem: result -= {i} # For part 2, this can be changed to < instead of <= for more speed. ¯\_(ツ)_/¯ # I guess I was just lucky with my input... if len(result) <= 2: result.add(-1) return result def find_max(bp_idx, blueprint, steps): c_ore, c_clay, c_ob_ore, c_ob_clay, c_geo_ore, c_geo_ob = blueprint q = PriorityQueue() states = {} results = {} purc_cache = {} res = (0, 0, 0, 0) robo = (1, 0, 0, 0) costs = [ (c_ore, 0, 0, 0), (c_clay, 0, 0, 0), (c_ob_ore, c_ob_clay, 0, 0), (c_geo_ore, 0, c_geo_ob, 0), ] max_costs = [max(c[i] for c in costs) for i in range(4)] start = (res, robo, steps) q.put(start) i = 0 while not q.empty(): current = q.get() #print(len(states), current) res, robo, rem = current if res[0] > i: i = res[0] print(i) if robo[0] >= c_geo_ore and robo[2] >= c_geo_ob: print("Early exit") states[current] = res[3] + robo[3] * rem + rem * rem // 2 continue if rem == 0: continue for robo_idx in possible_purchases(res, robo, rem, costs, max_costs): if robo_idx != -1: new_purc = [0, 0, 0, 0] new_purc[robo_idx] = 1 next_st = (add(sub(res, costs[robo_idx]), robo), add(robo, tuple(new_purc)), rem - 1) else: next_st = (add(res, robo), robo, rem - 1) if next_st not in states: states[next_st] = next_st[0][3] q.put(next_st) return max(s for s in states.values()) def part1(): total = 0 for i, bp in enumerate(blueprints): score = find_max(i, bp, 24) print(i + 1, score) total += (i + 1) * score print("Part 1:", total) part1() def part2(): product = 1 for i, bp in enumerate(blueprints[:3]): score = find_max(i, bp, 32) print(i + 1, score) product *= score print("Part 2:", product) part2()