From 2fb65387f79521b4d9bd1d2e130341427309eadb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Capelle?= Date: Tue, 5 Dec 2023 20:16:27 +0100 Subject: [PATCH] Backup local 2022 day 16. --- 2022/day16.py | 132 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 12 deletions(-) diff --git a/2022/day16.py b/2022/day16.py index 9af5a2c..f65220b 100644 --- a/2022/day16.py +++ b/2022/day16.py @@ -6,10 +6,11 @@ import heapq import itertools import re import sys +import time as time_p from collections import defaultdict from typing import FrozenSet, NamedTuple -from tqdm import tqdm +from tqdm import tqdm, trange class Pipe(NamedTuple): @@ -106,21 +107,128 @@ def part_1( def part_2( start_pipe: Pipe, max_time: int, - distances: dict[tuple[Pipe, Pipe], int], + pipes: dict[str, Pipe], relevant_pipes: FrozenSet[Pipe], + distances: dict[tuple[Pipe, Pipe], int], ): - def compute(pipes_for_me: FrozenSet[Pipe]) -> int: - return part_1(start_pipe, max_time, distances, pipes_for_me) + part_1( - start_pipe, max_time, distances, relevant_pipes - pipes_for_me + + node_at_times: dict[ + int, dict[tuple[Pipe, Pipe], dict[FrozenSet[Pipe], int]] + ] = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0))) + node_at_times[0] = {(start_pipe, start_pipe): {frozenset(): 0}} + + # map node + distance to + d1, d2, d3, d4 = 0, 0, 0, 0 + best_flow = 0 + + for time in range(max_time): + print( + f"{time + 1:2d}/{max_time} - {best_flow:4d} - " + f"{sum(map(len, node_at_times[time].values())):7d} - " + f"{d1:.3f} {d2:.3f} {d3:.3f} {d4:.3f}" ) - combs = [ - frozenset(relevant_pipes_1) - for r in range(2, len(relevant_pipes) // 2 + 1) - for relevant_pipes_1 in itertools.combinations(relevant_pipes, r) - ] + d1, d2, d3, d4 = 0, 0, 0, 0 + for (c_pipe, e_pipe), nodes in node_at_times[time].items(): + for flowing, flow in nodes.items(): - return max(compute(comb) for comb in tqdm(combs)) + t1 = time_p.time() + + c_best_flow = ( + flow + + sum(pipe.flow for pipe in flowing) * (max_time - time) + + sum( + ( + pipe.flow + * ( + max_time + - time + - 1 + - min(distances[c_pipe, pipe], distances[e_pipe, pipe]) + ) + for pipe in relevant_pipes + if pipe not in flowing + ), + start=0, + ) + ) + + d1 += time_p.time() - t1 + + if c_best_flow < best_flow: + continue + + best_flow = max( + best_flow, + flow + sum(pipe.flow for pipe in flowing) * (max_time - time), + ) + + t1 = time_p.time() + + if flowing != relevant_pipes: + for c_next_s, e_next_s in itertools.product( + c_pipe.tunnels, e_pipe.tunnels + ): + + c_next = pipes[c_next_s] + e_next = pipes[e_next_s] + update_with_better( + node_at_times[time + 1][c_next, e_next], + flow + sum(pipe.flow for pipe in flowing), + flowing, + ) + + d2 += time_p.time() - t1 + + t1 = time_p.time() + + if c_pipe in relevant_pipes and c_pipe not in flowing: + for e_next_s in e_pipe.tunnels: + + e_next = pipes[e_next_s] + + update_with_better( + node_at_times[time + 1][c_pipe, e_next], + flow + sum(pipe.flow for pipe in flowing), + flowing | {c_pipe}, + ) + + if e_pipe in relevant_pipes and e_pipe not in flowing: + for c_next_s in c_pipe.tunnels: + + c_next = pipes[c_next_s] + + update_with_better( + node_at_times[time + 1][c_next, e_pipe], + flow + sum(pipe.flow for pipe in flowing), + flowing | {e_pipe}, + ) + + if ( + e_pipe in relevant_pipes + and c_pipe in relevant_pipes + and e_pipe not in flowing + and c_pipe not in flowing + ): + update_with_better( + node_at_times[time + 1][c_pipe, e_pipe], + flow + sum(pipe.flow for pipe in flowing), + flowing | {c_pipe, e_pipe}, + ) + + update_with_better( + node_at_times[max_time][c_pipe, e_pipe], + flow + sum(pipe.flow for pipe in flowing) * (max_time - time), + flowing, + ) + + d3 += time_p.time() - t1 + + return max( + flow + for nodes_of_pipe in node_at_times[max_time].values() + for flow in nodes_of_pipe.values() + ) # === MAIN === @@ -159,4 +267,4 @@ relevant_pipes = frozenset(pipe for pipe in pipes.values() if pipe.flow > 0) print(part_1(pipes["AA"], 30, distances, relevant_pipes)) # 1707, 2223 -print(part_2(pipes["AA"], 26, distances, relevant_pipes)) +print(part_2(pipes["AA"], 26, pipes, relevant_pipes, distances))