Update ugly day 16.
This commit is contained in:
		
							
								
								
									
										250
									
								
								2022/day16.py
									
									
									
									
									
								
							
							
						
						
									
										250
									
								
								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}") | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user