From 15b987a590b5e8a9845ded4ccf2c60a68d2769fb Mon Sep 17 00:00:00 2001 From: Mikael CAPELLE Date: Fri, 16 Dec 2022 18:40:21 +0100 Subject: [PATCH] Update ugly day 16. --- 2022/day16.py | 250 +++++++++++++++++++++++++++++++++--------- 2022/inputs/day16.txt | 112 +++++++++---------- 2 files changed, 252 insertions(+), 110 deletions(-) diff --git a/2022/day16.py b/2022/day16.py index 6990b01..845ae3f 100644 --- a/2022/day16.py +++ b/2022/day16.py @@ -1,80 +1,221 @@ # -*- encoding: utf-8 -*- +from __future__ import annotations + +import heapq import itertools import re import sys +from collections import defaultdict +from typing import NamedTuple from docplex.mp.model import Model +from docplex.mp.vartype import BinaryVarType + + +class Pipe(NamedTuple): + name: str + flow: int + tunnels: list[str] + + def __lt__(self, other: object) -> bool: + return isinstance(other, Pipe) and other.name < self.name + + def __eq__(self, other: object) -> bool: + return isinstance(other, Pipe) and other.name == self.name + + def __hash__(self) -> int: + return hash(self.name) + + def __str__(self) -> str: + return self.name + + def __repr__(self) -> str: + return self.name + + +def breadth_first_search(pipes: dict[str, Pipe], pipe_1: Pipe, pipe_2: Pipe) -> int: + queue = [(0, pipe_1)] + visited = set() + + while queue: + distance, current = heapq.heappop(queue) + + if current in visited: + continue + + visited.add(current) + + if current == pipe_2: + return distance + + for tunnel in current.tunnels: + heapq.heappush(queue, (distance + 1, pipes[tunnel])) + + return -1 + lines = sys.stdin.read().splitlines() -pipes: dict[str, tuple[int, list[str]]] = {} + +pipes: dict[str, Pipe] = {} for line in lines: r = re.match( R"Valve ([A-Z]+) has flow rate=([0-9]+); tunnels? leads? to valves? (.+)", line, - ).groups() + ) + assert r - pipes[r[0]] = (int(r[1]), r[2].split(", ")) + g = r.groups() + + pipes[g[0]] = Pipe(g[0], int(g[1]), g[2].split(", ")) + +# compute distances from one valve to any other +distances: dict[tuple[Pipe, Pipe], int] = {} +for pipe_1 in pipes.values(): + for pipe_2 in pipes.values(): + distances[pipe_1, pipe_2] = breadth_first_search(pipes, pipe_1, pipe_2) + +# valves with flow +relevant_pipes = [pipe for pipe in pipes.values() if pipe.flow > 0] -start_pipe = "AA" -max_time = 30 +# nodes: list[tuple[Pipe, int, int, list[Pipe]]] = [(start_pipe, 0, 0, [])] +# best_flow: int = 0 + +# while nodes: +# current, time, flow, flowing = nodes.pop(0) + +# if time == max_time: +# if flow > best_flow: +# best_flow = flow +# continue + +# next_nodes: list[tuple[Pipe, int, int, list[Pipe]]] = [] +# for target in relevant_pipes: + +# if target is current or target in flowing: +# continue + +# distance = distances[current, target] + 1 + +# if time + distance >= max_time: +# continue + +# next_nodes.append( +# ( +# target, +# time + distance, +# flow + distance * sum(pipe.flow for pipe in flowing) + target.flow, +# flowing + [target], +# ) +# ) + +# # print(time, current, flow, next_nodes) + +# if not next_nodes: +# next_nodes.append( +# ( +# current, +# max_time, +# flow + sum(pipe.flow for pipe in flowing) * (max_time - time - 1), +# flowing, +# ) +# ) + +# nodes.extend(next_nodes) + +# # if time >= 4: +# # break + +# print(best_flow) + + +# nodes = [best] +# while nodes[-1].parent is not None: +# nodes.append(nodes[-1].parent) +# nodes = list(reversed(nodes)) + +# for node in nodes: +# print(node.time, node.valve, node.flow, node.flowing) # -max_time = 26 -ee = [0, 1] +start_pipe = pipes["AA"] +max_time = 30 +ee = [0] + +# max_time = 26 +# ee = [0, 1] m = Model() -present_at = m.binary_var_dict( - (e, t, pipe) for e, t, pipe in itertools.product(ee, range(max_time), pipes) -) -open_at = m.binary_var_dict( - (e, t, pipe) for e, t, pipe in itertools.product(ee, range(max_time), pipes) +var_out: dict[Pipe, dict[Pipe, BinaryVarType]] = { + pipe: m.binary_var_dict(relevant_pipes) for pipe in relevant_pipes + [start_pipe] +} +var_in: dict[Pipe, dict[Pipe, BinaryVarType]] = {pipe: {} for pipe in relevant_pipes} +for p1 in var_out: + for p2 in var_out[p1]: + var_in[p2][p1] = var_out[p1][p2] + +open_at: dict[tuple[int, Pipe], BinaryVarType] = m.continuous_var_dict( + ( + (t, pipe) + for t, pipe in itertools.product(range(max_time), [start_pipe] + relevant_pipes) + ), + lb=0, + ub=1, ) +for time, pipe in itertools.product(range(max_time), relevant_pipes): + m.add_constraint(open_at[time, pipe] <= m.sum()) + + for e in ee: - m.add_constraint(present_at[e, 0, start_pipe] == 1) + m.add_constraint(open_at[e, 0, start_pipe] == 1) -for e, pipe in itertools.product(ee, pipes): +for e, pipe in itertools.product(ee, relevant_pipes): m.add_constraint(open_at[e, 0, pipe] == 0) -for e, t in itertools.product(ee, range(max_time)): - m.add_constraint(m.sum(present_at[e, t, pipe] for pipe in pipes) == 1) +for e, t, p1 in itertools.product(ee, range(max_time), relevant_pipes): + from_time_and_pipe = [ + (p2, t - distances[p2, p1] - 1) + for p2 in relevant_pipes + [start_pipe] + if t - distances[p2, p1] - 1 >= 0 and p2 is not p1 + ] -for e, t, pipe in itertools.product(ee, range(1, max_time), pipes): + if from_time_and_pipe: + m.add_constraint( + open_at[e, t, p1] + <= m.sum(open_at[e, t2, p2] for p2, t2 in from_time_and_pipe) + ) + else: + m.add_constraint(open_at[e, t, p1] == 0) + +for pipe in relevant_pipes + [start_pipe]: m.add_constraint( - present_at[e, t, pipe] - <= present_at[e, t - 1, pipe] - + m.sum(present_at[e, t - 1, pipe2] for pipe2 in pipes[pipe][1]) + m.sum(open_at[e, t, pipe] for e, t in itertools.product(ee, range(max_time))) + <= 1 + ) +for e, t in itertools.product(ee, range(max_time)): + m.add_constraint( + m.sum(open_at[e, t, pipe] for pipe in relevant_pipes + [start_pipe]) <= 1 ) - - for pipe2 in pipes: - if pipe2 != pipe: - m.add_constraint(present_at[e, t, pipe] <= 1 - open_at[e, t - 1, pipe2]) - -for e, t, pipe in itertools.product(ee, range(1, max_time), pipes): - m.add_constraint(open_at[e, t, pipe] <= present_at[e, t, pipe]) # keeps flowing -flowing_at = m.binary_var_dict( - (t, pipe) for t, pipe in itertools.product(range(max_time), pipes) -) - -for t, pipe in itertools.product(range(max_time), pipes): - m.add_constraint( - flowing_at[t, pipe] - == m.sum(open_at[e, t2, pipe] for e, t2 in itertools.product(ee, range(0, t))) +flowing_at = { + (t, pipe): m.sum( + open_at[e, t2, pipe] for e, t2 in itertools.product(ee, range(0, t)) ) + for t, pipe in itertools.product(range(max_time), relevant_pipes) +} # objective m.set_objective( "max", m.sum( - flowing_at[t, pipe] * pipes[pipe][0] - for t, pipe in itertools.product(range(max_time), pipes) + flowing_at[t, pipe] * pipe.flow + for t, pipe in itertools.product(range(max_time), relevant_pipes) ), ) @@ -84,22 +225,23 @@ s = m.solve() print(s.get_objective_value()) -# for t in range(max_time): -# present = { -# e: [pipe for pipe in pipes if s.get_value(present_at[e, t, pipe]) > 1e-6] -# for e in ee -# } -# opent = { -# e: [pipe for pipe in pipes if s.get_value(open_at[e, t, pipe]) > 1e-6] -# for e in ee -# } -# flowing = [ -# pipe -# for pipe in pipes -# if any(s.get_value(flowing_at[e, t, pipe]) > 1e-6 for e in ee) -# ] +for t in range(max_time): + opent = { + e: [ + pipe + for pipe in relevant_pipes + [start_pipe] + if s.get_value(open_at[e, t, pipe]) > 1e-8 + ] + for e in ee + } + flowing = [ + pipe + for pipe in relevant_pipes + if any(s.get_value(flowing_at[t, pipe]) > 1e-8 for e in ee) + ] -# p = [present[e][0] for e in ee] -# o = [opent[e][0] if opent[e] else "-" for e in ee] + assert all(len(opent[e]) <= 1 for e in ee) -# print(f"t={t}, at={p}, open={o}, flowing={flowing}") + o = [opent[e][0] if opent[e] else "-" for e in ee] + + print(f"t={t}, open={o}, flowing={flowing}") diff --git a/2022/inputs/day16.txt b/2022/inputs/day16.txt index 2727099..a641a7b 100644 --- a/2022/inputs/day16.txt +++ b/2022/inputs/day16.txt @@ -1,64 +1,64 @@ -Valve MU has flow rate=0; tunnels lead to valves VT, LA -Valve TQ has flow rate=0; tunnels lead to valves HU, SU -Valve YH has flow rate=0; tunnels lead to valves CN, BN -Valve EO has flow rate=0; tunnels lead to valves IK, CN -Valve MH has flow rate=0; tunnels lead to valves GG, HG -Valve RJ has flow rate=0; tunnels lead to valves AA, RI -Valve XZ has flow rate=0; tunnels lead to valves PX, VT -Valve UU has flow rate=0; tunnels lead to valves DT, XG -Valve KV has flow rate=13; tunnels lead to valves HN, CV, PE, XD, TA -Valve SU has flow rate=19; tunnels lead to valves TQ, HF, OL, SF -Valve BB has flow rate=0; tunnels lead to valves NS, HR -Valve RI has flow rate=4; tunnels lead to valves ML, EE, TZ, RJ, PE -Valve TZ has flow rate=0; tunnels lead to valves VT, RI -Valve LY has flow rate=0; tunnels lead to valves EE, RP -Valve PX has flow rate=0; tunnels lead to valves XZ, JQ -Valve VH has flow rate=0; tunnels lead to valves DT, TA -Valve HN has flow rate=0; tunnels lead to valves KV, LR -Valve LR has flow rate=0; tunnels lead to valves HR, HN -Valve NJ has flow rate=0; tunnels lead to valves QF, JC +Valve AA has flow rate=0; tunnels lead to valves AM, AN, BN, LA, RJ Valve AM has flow rate=0; tunnels lead to valves OJ, AA -Valve FM has flow rate=0; tunnels lead to valves VT, RP -Valve VT has flow rate=5; tunnels lead to valves IP, XZ, TZ, FM, MU -Valve HF has flow rate=0; tunnels lead to valves NR, SU -Valve HR has flow rate=11; tunnels lead to valves BB, KO, LR -Valve WX has flow rate=0; tunnels lead to valves CN, IP -Valve PE has flow rate=0; tunnels lead to valves KV, RI -Valve QF has flow rate=17; tunnels lead to valves YI, NJ -Valve EE has flow rate=0; tunnels lead to valves LY, RI -Valve UH has flow rate=25; tunnel leads to valve YI -Valve CV has flow rate=0; tunnels lead to valves KV, NS -Valve SF has flow rate=0; tunnels lead to valves YN, SU -Valve RP has flow rate=3; tunnels lead to valves HG, FM, OJ, IK, LY -Valve XD has flow rate=0; tunnels lead to valves IL, KV -Valve GG has flow rate=12; tunnels lead to valves ML, IL, MH, OL, KA -Valve XG has flow rate=0; tunnels lead to valves LI, UU -Valve YA has flow rate=21; tunnels lead to valves UJ, GQ -Valve OL has flow rate=0; tunnels lead to valves GG, SU Valve AN has flow rate=0; tunnels lead to valves AA, IX -Valve LI has flow rate=15; tunnel leads to valve XG +Valve BB has flow rate=0; tunnels lead to valves NS, HR +Valve BN has flow rate=0; tunnels lead to valves AA, YH +Valve CN has flow rate=7; tunnels lead to valves YH, EO, WX, NR, OM +Valve CV has flow rate=0; tunnels lead to valves KV, NS +Valve DT has flow rate=16; tunnels lead to valves UU, HU, KA, VH +Valve EE has flow rate=0; tunnels lead to valves LY, RI +Valve EO has flow rate=0; tunnels lead to valves IK, CN +Valve FM has flow rate=0; tunnels lead to valves VT, RP +Valve GG has flow rate=12; tunnels lead to valves ML, IL, MH, OL, KA Valve GQ has flow rate=0; tunnels lead to valves YA, KO +Valve HF has flow rate=0; tunnels lead to valves NR, SU +Valve HG has flow rate=0; tunnels lead to valves MH, RP +Valve HN has flow rate=0; tunnels lead to valves KV, LR +Valve HR has flow rate=11; tunnels lead to valves BB, KO, LR Valve HU has flow rate=0; tunnels lead to valves TQ, DT -Valve OJ has flow rate=0; tunnels lead to valves RP, AM -Valve YN has flow rate=0; tunnels lead to valves SF, JQ -Valve ML has flow rate=0; tunnels lead to valves RI, GG -Valve UJ has flow rate=0; tunnels lead to valves YA, NS +Valve IK has flow rate=0; tunnels lead to valves EO, RP +Valve IL has flow rate=0; tunnels lead to valves GG, XD +Valve IP has flow rate=0; tunnels lead to valves WX, VT Valve IX has flow rate=0; tunnels lead to valves AN, JQ Valve JC has flow rate=0; tunnels lead to valves JQ, NJ -Valve TA has flow rate=0; tunnels lead to valves KV, VH -Valve DT has flow rate=16; tunnels lead to valves UU, HU, KA, VH -Valve NR has flow rate=0; tunnels lead to valves HF, CN -Valve YI has flow rate=0; tunnels lead to valves QF, UH -Valve AA has flow rate=0; tunnels lead to valves AM, AN, BN, LA, RJ -Valve BN has flow rate=0; tunnels lead to valves AA, YH -Valve KA has flow rate=0; tunnels lead to valves GG, DT -Valve IL has flow rate=0; tunnels lead to valves GG, XD -Valve CN has flow rate=7; tunnels lead to valves YH, EO, WX, NR, OM -Valve IP has flow rate=0; tunnels lead to valves WX, VT -Valve OM has flow rate=0; tunnels lead to valves CN, JQ -Valve KO has flow rate=0; tunnels lead to valves GQ, HR -Valve LA has flow rate=0; tunnels lead to valves AA, MU Valve JQ has flow rate=6; tunnels lead to valves IX, JC, PX, YN, OM -Valve IK has flow rate=0; tunnels lead to valves EO, RP -Valve HG has flow rate=0; tunnels lead to valves MH, RP +Valve KA has flow rate=0; tunnels lead to valves GG, DT +Valve KO has flow rate=0; tunnels lead to valves GQ, HR +Valve KV has flow rate=13; tunnels lead to valves HN, CV, PE, XD, TA +Valve LA has flow rate=0; tunnels lead to valves AA, MU +Valve LI has flow rate=15; tunnel leads to valve XG +Valve LR has flow rate=0; tunnels lead to valves HR, HN +Valve LY has flow rate=0; tunnels lead to valves EE, RP +Valve MH has flow rate=0; tunnels lead to valves GG, HG +Valve ML has flow rate=0; tunnels lead to valves RI, GG +Valve MU has flow rate=0; tunnels lead to valves VT, LA +Valve NJ has flow rate=0; tunnels lead to valves QF, JC +Valve NR has flow rate=0; tunnels lead to valves HF, CN Valve NS has flow rate=23; tunnels lead to valves CV, BB, UJ +Valve OJ has flow rate=0; tunnels lead to valves RP, AM +Valve OL has flow rate=0; tunnels lead to valves GG, SU +Valve OM has flow rate=0; tunnels lead to valves CN, JQ +Valve PE has flow rate=0; tunnels lead to valves KV, RI +Valve PX has flow rate=0; tunnels lead to valves XZ, JQ +Valve QF has flow rate=17; tunnels lead to valves YI, NJ +Valve RI has flow rate=4; tunnels lead to valves ML, EE, TZ, RJ, PE +Valve RJ has flow rate=0; tunnels lead to valves AA, RI +Valve RP has flow rate=3; tunnels lead to valves HG, FM, OJ, IK, LY +Valve SF has flow rate=0; tunnels lead to valves YN, SU +Valve SU has flow rate=19; tunnels lead to valves TQ, HF, OL, SF +Valve TA has flow rate=0; tunnels lead to valves KV, VH +Valve TQ has flow rate=0; tunnels lead to valves HU, SU +Valve TZ has flow rate=0; tunnels lead to valves VT, RI +Valve UH has flow rate=25; tunnel leads to valve YI +Valve UJ has flow rate=0; tunnels lead to valves YA, NS +Valve UU has flow rate=0; tunnels lead to valves DT, XG +Valve VH has flow rate=0; tunnels lead to valves DT, TA +Valve VT has flow rate=5; tunnels lead to valves IP, XZ, TZ, FM, MU +Valve WX has flow rate=0; tunnels lead to valves CN, IP +Valve XD has flow rate=0; tunnels lead to valves IL, KV +Valve XG has flow rate=0; tunnels lead to valves LI, UU +Valve XZ has flow rate=0; tunnels lead to valves PX, VT +Valve YA has flow rate=21; tunnels lead to valves UJ, GQ +Valve YH has flow rate=0; tunnels lead to valves CN, BN +Valve YI has flow rate=0; tunnels lead to valves QF, UH +Valve YN has flow rate=0; tunnels lead to valves SF, JQ