2022-16
Signed-off-by: AKP <tom@tdpain.net>
This commit is contained in:
parent
dd8086f431
commit
c1505bb80f
6 changed files with 189 additions and 1 deletions
3
challenges/2022/16-proboscideaVolcanium/README.md
Normal file
3
challenges/2022/16-proboscideaVolcanium/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# [Day 16: Proboscidea Volcanium](https://adventofcode.com/2022/day/16)
|
||||
|
||||
[This solution was used to end up with my solution](https://github.com/davearussell/advent2022/blob/master/day16/solve.py).
|
15
challenges/2022/16-proboscideaVolcanium/benchmark.json
Normal file
15
challenges/2022/16-proboscideaVolcanium/benchmark.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"day": 16,
|
||||
"dir": "challenges/2022/16-proboscideaVolcanium",
|
||||
"implementations": {
|
||||
"Python": {
|
||||
"part.1.avg": 3.520505666732788,
|
||||
"part.1.max": 3.520505666732788,
|
||||
"part.1.min": 3.520505666732788,
|
||||
"part.2.avg": 18.672574281692505,
|
||||
"part.2.max": 18.672574281692505,
|
||||
"part.2.min": 18.672574281692505
|
||||
}
|
||||
},
|
||||
"numRuns": 1
|
||||
}
|
17
challenges/2022/16-proboscideaVolcanium/info.json
Normal file
17
challenges/2022/16-proboscideaVolcanium/info.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"inputFile": "input.txt",
|
||||
"testCases": {
|
||||
"one": [
|
||||
{
|
||||
"input": "Valve AA has flow rate=0; tunnels lead to valves DD, II, BB\nValve BB has flow rate=13; tunnels lead to valves CC, AA\nValve CC has flow rate=2; tunnels lead to valves DD, BB\nValve DD has flow rate=20; tunnels lead to valves CC, AA, EE\nValve EE has flow rate=3; tunnels lead to valves FF, DD\nValve FF has flow rate=0; tunnels lead to valves EE, GG\nValve GG has flow rate=0; tunnels lead to valves FF, HH\nValve HH has flow rate=22; tunnel leads to valve GG\nValve II has flow rate=0; tunnels lead to valves AA, JJ\nValve JJ has flow rate=21; tunnel leads to valve II\n",
|
||||
"expected": "1651"
|
||||
}
|
||||
],
|
||||
"two": [
|
||||
{
|
||||
"input": "Valve AA has flow rate=0; tunnels lead to valves DD, II, BB\nValve BB has flow rate=13; tunnels lead to valves CC, AA\nValve CC has flow rate=2; tunnels lead to valves DD, BB\nValve DD has flow rate=20; tunnels lead to valves CC, AA, EE\nValve EE has flow rate=3; tunnels lead to valves FF, DD\nValve FF has flow rate=0; tunnels lead to valves EE, GG\nValve GG has flow rate=0; tunnels lead to valves FF, HH\nValve HH has flow rate=22; tunnel leads to valve GG\nValve II has flow rate=0; tunnels lead to valves AA, JJ\nValve JJ has flow rate=21; tunnel leads to valve II\n",
|
||||
"expected": "1707"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
152
challenges/2022/16-proboscideaVolcanium/py/__init__.py
Normal file
152
challenges/2022/16-proboscideaVolcanium/py/__init__.py
Normal file
|
@ -0,0 +1,152 @@
|
|||
from __future__ import annotations
|
||||
from pprint import pprint
|
||||
from typing import *
|
||||
from aocpy import BaseChallenge
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
from math import inf
|
||||
import itertools
|
||||
import copy
|
||||
|
||||
@dataclass
|
||||
class Node:
|
||||
name: str
|
||||
value: int
|
||||
edges: List[Node]
|
||||
|
||||
def __init__(self, name: str, value: int = 0, edges: Optional[List[Tuple[Node, int]]] = None):
|
||||
self.name = name
|
||||
self.value = value
|
||||
self.edges = [] if edges is None else edges
|
||||
|
||||
|
||||
def shortest_path(nodes: Dict[str, Node], begin: str, end: str) -> List[str]:
|
||||
priorities: Dict[str, Tuple[Union[int, float], Optional[str]]] = {node: (inf, None) for node in nodes}
|
||||
visited: Dict[str, None] = {begin: None}
|
||||
|
||||
cursor = begin
|
||||
while True:
|
||||
if cursor == end:
|
||||
break
|
||||
|
||||
n = nodes[cursor]
|
||||
visited[cursor] = None
|
||||
|
||||
length_to_current = priorities[cursor][0]
|
||||
if length_to_current == inf:
|
||||
length_to_current = 0
|
||||
|
||||
# every neighbour that's not been visited already
|
||||
for neighbour in n.edges:
|
||||
if neighbour.name in visited:
|
||||
continue
|
||||
if priorities[neighbour.name][0] > length_to_current + 1:
|
||||
priorities[neighbour.name] = (length_to_current + 1, cursor)
|
||||
|
||||
# work out next item
|
||||
min_priority = (inf, None)
|
||||
for node_name in priorities:
|
||||
if node_name not in visited and priorities[node_name][0] < min_priority[0]:
|
||||
min_priority = (priorities[node_name][0], node_name)
|
||||
|
||||
cursor = min_priority[1]
|
||||
|
||||
route: List[str] = []
|
||||
while priorities[cursor][1] is not None:
|
||||
route.insert(0, cursor)
|
||||
cursor = priorities[cursor][1]
|
||||
|
||||
return route[:-1]
|
||||
|
||||
|
||||
parse_re = re.compile(r"Valve ([A-Z]+) has flow rate=(\d+); tunnels? leads? to valves? ((?:[A-Z]+,? ?)+)")
|
||||
|
||||
|
||||
def parse(instr: str) -> Tuple[Dict[str, Node], List[str], Dict[Tuple[str, str], List[str]]]:
|
||||
sp = [parse_re.match(line).groups() for line in instr.strip().splitlines()]
|
||||
|
||||
nodes: Dict[str, Node] = {}
|
||||
unjammed_nodes: List[str] = []
|
||||
for (valve_name, flow_rate_str, _) in sp:
|
||||
flow_rate = int(flow_rate_str)
|
||||
nodes[valve_name] = Node(valve_name, flow_rate)
|
||||
if flow_rate != 0:
|
||||
unjammed_nodes.append(valve_name)
|
||||
|
||||
for (valve_name, _, further_nodes_str) in sp:
|
||||
n = nodes[valve_name]
|
||||
for connected_node_name in further_nodes_str.split(", "):
|
||||
n.edges.append(nodes[connected_node_name])
|
||||
|
||||
# work out a matrix of the shortest paths between two nodes
|
||||
shortest_paths: Dict[Tuple[str, str], List[str]] = {}
|
||||
for start_node in nodes:
|
||||
|
||||
if nodes[start_node].value == 0 and start_node != "AA":
|
||||
continue
|
||||
|
||||
for end_node in nodes:
|
||||
if end_node == start_node or nodes[end_node].value == 0:
|
||||
continue
|
||||
|
||||
path = shortest_path(nodes, start_node, end_node)
|
||||
pl = len(path) + 1
|
||||
shortest_paths[(start_node, end_node)] = pl
|
||||
shortest_paths[(end_node, start_node)] = pl
|
||||
|
||||
return nodes, unjammed_nodes, shortest_paths
|
||||
|
||||
|
||||
def permutations(current_node: str, nodes_remaining: List[str], shortest_paths: Dict[Tuple[str, str], List[str]], path: List[str], cost_remaining: int) -> Generator[List[str]]:
|
||||
for next_node in nodes_remaining:
|
||||
cost = shortest_paths[(current_node, next_node)]
|
||||
if cost < cost_remaining:
|
||||
nr = copy.copy(nodes_remaining)
|
||||
nr.remove(next_node)
|
||||
yield from permutations(next_node, nr, shortest_paths, path + [next_node], cost_remaining - cost)
|
||||
yield path
|
||||
|
||||
def calc_vented(nodes: Dict[str, Node], shortest_paths: Dict[Tuple[str, str], List[str]], visit_order: List[str], time_remaining: int) -> int:
|
||||
current = "AA"
|
||||
pressure = 0
|
||||
|
||||
for node_name in visit_order:
|
||||
path_length = shortest_paths[(current, node_name)]
|
||||
time_remaining -= path_length + 1
|
||||
pressure += nodes[node_name].value * time_remaining
|
||||
current = node_name
|
||||
|
||||
return pressure
|
||||
|
||||
class Challenge(BaseChallenge):
|
||||
|
||||
@staticmethod
|
||||
def one(instr: str) -> int:
|
||||
nodes, unjammed_nodes, shortest_paths = parse(instr)
|
||||
|
||||
max_pressure = 0
|
||||
for visit_order in permutations("AA", unjammed_nodes, shortest_paths, [], 30):
|
||||
pressure = calc_vented(nodes, shortest_paths, visit_order, 30)
|
||||
|
||||
if pressure > max_pressure:
|
||||
max_pressure = pressure
|
||||
|
||||
return max_pressure
|
||||
|
||||
@staticmethod
|
||||
def two(instr: str) -> int:
|
||||
nodes, unjammed_nodes, shortest_paths = parse(instr)
|
||||
|
||||
pressures = [(calc_vented(nodes, shortest_paths, visit_order, 26), visit_order) for visit_order in permutations("AA", unjammed_nodes, shortest_paths, [], 26)]
|
||||
|
||||
max_pressure = 0
|
||||
for i, (pressure_a, order_a) in enumerate(sorted(pressures, reverse=True, key=lambda x: x[0])):
|
||||
if pressure_a * 2 < max_pressure:
|
||||
break
|
||||
for (pressure_b, order_b) in pressures[i+1:]:
|
||||
if len(set(order_a).intersection(order_b)) == 0:
|
||||
pressure = pressure_a + pressure_b
|
||||
if pressure > max_pressure:
|
||||
max_pressure = pressure
|
||||
|
||||
return max_pressure
|
|
@ -28,4 +28,5 @@ Solutions to the [2022 Advent of Code](https://adventofcode.com/2022).
|
|||
| 12 - Hill Climbing Algorithm | ☆ ☆ | | |
|
||||
| 13 - Distress Signal | ☆ ☆ | | |
|
||||
| 14 - Regolith Reservoir | ★ ★ | [Python](14-regolithReservoir/py) | Simulating falling sand |
|
||||
| 15 - Beacon Exclusion Zone | ★ ★ | [Python](15-beaconExclusionZone/py) | Searching through a 4000000^2 size grid for a literal single empty spot |
|
||||
| 15 - Beacon Exclusion Zone | ★ ★ | [Python](15-beaconExclusionZone/py) | Searching through a 4000000^2 size grid for a literal single empty spot |
|
||||
| 16 - Proboscidea Volcanium | ★ ★ | [Python](16-proboscideaVolcanium/py) | Nasty combinatorics |
|
Binary file not shown.
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 52 KiB |
Loading…
Add table
Add a link
Reference in a new issue