From 87287d9f2e6672c5fab15d64f51b6b40c7c5fb38 Mon Sep 17 00:00:00 2001 From: daviddoji Date: Fri, 24 Nov 2023 20:20:43 +0100 Subject: [PATCH] Solution to problem 16 in Python --- src/Year_2022/Day16.py | 360 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 src/Year_2022/Day16.py diff --git a/src/Year_2022/Day16.py b/src/Year_2022/Day16.py new file mode 100644 index 0000000..862cd17 --- /dev/null +++ b/src/Year_2022/Day16.py @@ -0,0 +1,360 @@ +# --- Day 16: Proboscidea Volcanium --- + +# The sensors have led you to the origin of the distress signal: yet another +# handheld device, just like the one the Elves gave you. However, you don't see +# any Elves around; instead, the device is surrounded by elephants! They must +# have gotten lost in these tunnels, and one of the elephants apparently figured +# out how to turn on the distress signal. + +# The ground rumbles again, much stronger this time. What kind of cave is this, +# exactly? You scan the cave with your handheld device; it reports mostly +# igneous rock, some ash, pockets of pressurized gas, magma... this isn't just a +# cave, it's a volcano! + +# You need to get the elephants out of here, quickly. Your device estimates that +# you have 30 minutes before the volcano erupts, so you don't have time to go +# back out the way you came in. + +# You scan the cave for other options and discover a network of pipes and +# pressure-release valves. You aren't sure how such a system got into a volcano, +# but you don't have time to complain; your device produces a report (your +# puzzle input) of each valve's flow rate if it were opened (in pressure per +# minute) and the tunnels you could use to move between the valves. + +# There's even a valve in the room you and the elephants are currently standing +# in labeled AA. You estimate it will take you one minute to open a single valve +# and one minute to follow any tunnel from one valve to another. What is the +# most pressure you could release? + +# For example, suppose you had the following scan output: + +# Valve AA has flow rate=0; tunnels lead to valves DD, II, BB +# Valve BB has flow rate=13; tunnels lead to valves CC, AA +# Valve CC has flow rate=2; tunnels lead to valves DD, BB +# Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE +# Valve EE has flow rate=3; tunnels lead to valves FF, DD +# Valve FF has flow rate=0; tunnels lead to valves EE, GG +# Valve GG has flow rate=0; tunnels lead to valves FF, HH +# Valve HH has flow rate=22; tunnel leads to valve GG +# Valve II has flow rate=0; tunnels lead to valves AA, JJ +# Valve JJ has flow rate=21; tunnel leads to valve II + +# All of the valves begin closed. You start at valve AA, but it must be damaged +# or jammed or something: its flow rate is 0, so there's no point in opening it. +# However, you could spend one minute moving to valve BB and another minute +# opening it; doing so would release pressure during the remaining 28 minutes at +# a flow rate of 13, a total eventual pressure release of 28 * 13 = 364. Then, +# you could spend your third minute moving to valve CC and your fourth minute +# opening it, providing an additional 26 minutes of eventual pressure release at +# a flow rate of 2, or 52 total pressure released by valve CC. + +# Making your way through the tunnels like this, you could probably open many or +# all of the valves by the time 30 minutes have elapsed. However, you need to +# release as much pressure as possible, so you'll need to be methodical. +# Instead, consider this approach: + +# == Minute 1 == +# No valves are open. +# You move to valve DD. + +# == Minute 2 == +# No valves are open. +# You open valve DD. + +# == Minute 3 == +# Valve DD is open, releasing 20 pressure. +# You move to valve CC. + +# == Minute 4 == +# Valve DD is open, releasing 20 pressure. +# You move to valve BB. + +# == Minute 5 == +# Valve DD is open, releasing 20 pressure. +# You open valve BB. + +# == Minute 6 == +# Valves BB and DD are open, releasing 33 pressure. +# You move to valve AA. + +# == Minute 7 == +# Valves BB and DD are open, releasing 33 pressure. +# You move to valve II. + +# == Minute 8 == +# Valves BB and DD are open, releasing 33 pressure. +# You move to valve JJ. + +# == Minute 9 == +# Valves BB and DD are open, releasing 33 pressure. +# You open valve JJ. + +# == Minute 10 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve II. + +# == Minute 11 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve AA. + +# == Minute 12 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve DD. + +# == Minute 13 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve EE. + +# == Minute 14 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve FF. + +# == Minute 15 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve GG. + +# == Minute 16 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You move to valve HH. + +# == Minute 17 == +# Valves BB, DD, and JJ are open, releasing 54 pressure. +# You open valve HH. + +# == Minute 18 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You move to valve GG. + +# == Minute 19 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You move to valve FF. + +# == Minute 20 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You move to valve EE. + +# == Minute 21 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You open valve EE. + +# == Minute 22 == +# Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure. +# You move to valve DD. + +# == Minute 23 == +# Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure. +# You move to valve CC. + +# == Minute 24 == +# Valves BB, DD, EE, HH, and JJ are open, releasing 79 pressure. +# You open valve CC. + +# == Minute 25 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# == Minute 26 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# == Minute 27 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# == Minute 28 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# == Minute 29 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# == Minute 30 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# This approach lets you release the most pressure possible in 30 minutes with +# this valve layout, 1651. + +# Work out the steps to release the most pressure in 30 minutes. What is the +# most pressure you can release? + + +import re +from collections import deque + +with open("files/P16.txt") as f: + data = f.read().splitlines() + rates = {} + tunnels = {} + for line in data: + parts = line.split() + valve = parts[1] + rate = int(re.search("\d+", line).group()) + if rate: + rates[valve] = rate + tunnel_list = re.search(r"valves? (.+)$", line).group(1) + tunnels[valve] = tunnel_list.split(", ") + + +def bfs(tunnels, start, targets): + dist = {start: 0} + seen = {start} + queue = deque([start]) + while queue and any(target not in dist for target in targets): + path = queue.popleft() + for route in tunnels[path]: + if route not in seen: + seen.add(route) + dist[route] = dist[path] + 1 + queue.append(route) + return dist + + +def find_paths(dist, rates, time): + pressures = [] + paths = [] + stack = [(time, 0, ["AA"])] + while stack: + time, p, path = stack.pop() + cur = path[-1] + new = [] + for n, d in dist[cur].items(): + if d > time - 2 or n in path: + continue + tt = time - d - 1 + pp = p + rates[n] * tt + s = tt, pp, path + [n] + new.append(s) + if new: + stack.extend(new) + else: + pressures.append(p) + # paths always start at AA, so no need to keep first location + paths.append(path[1:]) + return pressures, paths + + +def get_distances(): + dist = {} + for start in ("AA", *rates): + dist[start] = {} + d = bfs(tunnels, start, rates) + for rate in rates: + if rate != start and rate in d: + dist[start][rate] = d[rate] + + return dist + + +def part_1(): + dist = get_distances() + p, _ = find_paths(dist, rates, 30) + print(f"One can release up to {max(p)} pressure") + + +# --- Part Two --- + +# You're worried that even with an optimal approach, the pressure released won't +# be enough. What if you got one of the elephants to help you? + +# It would take you 4 minutes to teach an elephant how to open the right valves +# in the right order, leaving you with only 26 minutes to actually execute your +# plan. Would having two of you working together be better, even if it means +# having less time? (Assume that you teach the elephant before opening any +# valves yourself, giving you both the same full 26 minutes.) + +# In the example above, you could teach the elephant to help you as follows: + +# == Minute 1 == +# No valves are open. +# You move to valve II. +# The elephant moves to valve DD. + +# == Minute 2 == +# No valves are open. +# You move to valve JJ. +# The elephant opens valve DD. + +# == Minute 3 == +# Valve DD is open, releasing 20 pressure. +# You open valve JJ. +# The elephant moves to valve EE. + +# == Minute 4 == +# Valves DD and JJ are open, releasing 41 pressure. +# You move to valve II. +# The elephant moves to valve FF. + +# == Minute 5 == +# Valves DD and JJ are open, releasing 41 pressure. +# You move to valve AA. +# The elephant moves to valve GG. + +# == Minute 6 == +# Valves DD and JJ are open, releasing 41 pressure. +# You move to valve BB. +# The elephant moves to valve HH. + +# == Minute 7 == +# Valves DD and JJ are open, releasing 41 pressure. +# You open valve BB. +# The elephant opens valve HH. + +# == Minute 8 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You move to valve CC. +# The elephant moves to valve GG. + +# == Minute 9 == +# Valves BB, DD, HH, and JJ are open, releasing 76 pressure. +# You open valve CC. +# The elephant moves to valve FF. + +# == Minute 10 == +# Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. +# The elephant moves to valve EE. + +# == Minute 11 == +# Valves BB, CC, DD, HH, and JJ are open, releasing 78 pressure. +# The elephant opens valve EE. + +# (At this point, all valves are open.) + +# == Minute 12 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# ... + +# == Minute 20 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# ... + +# == Minute 26 == +# Valves BB, CC, DD, EE, HH, and JJ are open, releasing 81 pressure. + +# With the elephant helping, after 26 minutes, the best you could do would +# release a total of 1707 pressure. + +# With you and an elephant working together for 26 minutes, what is the most +# pressure you could release? + + +def part_2(): + dist = get_distances() + # # Part Two + all_paths = list(zip(*find_paths(dist, rates, 26))) + # p, _ = find_paths(dist, rates, 26) + p, paths = zip(*sorted(all_paths, reverse=True)) + i, j = 0, 1 + while any(path in paths[j] for path in paths[i]): + j += 1 + combined_p = p[i] + p[j] # lower bound + j_max = j # since p[i] can only decrease, j cannot exceed this + for i in range(1, j_max): + for j in range(i + 1, j_max + 1): + if any(path in paths[j] for path in paths[i]): + continue + combined_p = max(combined_p, p[i] + p[j]) + # print(ans) + print(f"An elephant and me could release up to {combined_p} pressure") + + +if __name__ == "__main__": + part_1() + part_2()