From db2a2f451ed9c2af362887fd06e32fb119482fdb Mon Sep 17 00:00:00 2001 From: Luke Hubmayer-Werner Date: Mon, 19 Dec 2022 23:15:42 +1030 Subject: [PATCH] slow python --- 2022/day19.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 2022/day19.py diff --git a/2022/day19.py b/2022/day19.py new file mode 100644 index 0000000..96b4eb3 --- /dev/null +++ b/2022/day19.py @@ -0,0 +1,70 @@ +from helpers import * +from multiprocessing import Pool +lines = read_day(19).split('\n') + +sample_lines = ''' +Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian. +Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian. +'''.strip().split('\n') + +maximum_potential = np.arange(32).cumsum() + +def bp_quality(line, t_max=24): + bp, cost_orebot_ore, cost_claybot_ore, cost_obsbot_ore, cost_obsbot_clay, cost_geodebot_ore, cost_geodebot_obs = line_to_numbers(line) + print(line) + # Ore, Clay, Obsidian, Geode + orebot_cost = np.array([cost_orebot_ore, 0, 0], dtype=np.int8) + claybot_cost = np.array([cost_claybot_ore, 0, 0], dtype=np.int8) + obsbot_cost = np.array([cost_obsbot_ore, cost_obsbot_clay, 0], dtype=np.int8) + geodebot_cost = np.array([cost_geodebot_ore, 0, cost_geodebot_obs], dtype=np.int8) + robot_orders = ( # Special ordering to try and hit best case earliest + ((0,1,0), claybot_cost), + ((1,0,0), orebot_cost), + ((0,0,1), obsbot_cost), + (None, geodebot_cost), + ) + + search_stack = [] + SEEN = set() + def add_state(t, robot_counts, res_counts, geodes_total): + state = (t, tuple(robot_counts), tuple(res_counts), geodes_total) + if state not in SEEN: + search_stack.append((t, robot_counts, res_counts, geodes_total)) + SEEN.add(state) + + add_state(0, np.array((1,0,0), dtype=np.int8), np.array((0,0,0), dtype=np.int8), 0) + geodes_best = 0 + while len(search_stack) > 0: + t, robot_counts, res_counts, geodes_total = search_stack.pop() + if (geodes_total + maximum_potential[t_max-t]) < geodes_best: + continue + if t < t_max: + for inc, cost in robot_orders: + mask = cost > 0 # Only look at resources within the cost + if np.any(robot_counts[mask] == 0): # no robot no income, we can't just wait to build it + continue + time_to_resources = max(-(-(cost[mask] - res_counts[mask]) // robot_counts[mask])) + dt = max(0, time_to_resources) + 1 # Always need one minute to build something after having the resources ready + nt = t + dt + if nt < t_max: # Nothing interesting can happen on the very last minute + new_res = res_counts + (dt * robot_counts) - cost + if inc is None: # Special case for Geodebots + add_state(nt, robot_counts, new_res, geodes_total+(t_max-nt)) + else: + add_state(nt, robot_counts + inc, new_res, geodes_total) + geodes_best = max(geodes_best, geodes_total) + + quality = bp * geodes_best + print(f'Blueprint {bp}: max geodes {geodes_best}, quality number = {quality}') + return bp, geodes_best, quality + + +if __name__ == '__main__': + # with Pool(8) as p: + # # Maximize geodes in 24 minutes + # qual_tally = sum([quality for bp, max_geodes, quality in p.map(bp_quality, lines)]) + # print(f'Part 1: {qual_tally}') + # max_prod = prod([max_geodes for bp, max_geodes, quality in p.map(lambda x: bp_quality(x, 32), lines[:3])]) + # print(f'Part 2: {max_prod}') + qual_tally = sum((bp_quality(line)[-1] for line in lines)) + print(f'Part 1: {qual_tally}')