aboutsummaryrefslogtreecommitdiffstats
path: root/day16/solution.nim
diff options
context:
space:
mode:
Diffstat (limited to 'day16/solution.nim')
-rw-r--r--day16/solution.nim118
1 files changed, 118 insertions, 0 deletions
diff --git a/day16/solution.nim b/day16/solution.nim
new file mode 100644
index 0000000..b6d39e6
--- /dev/null
+++ b/day16/solution.nim
@@ -0,0 +1,118 @@
1import strutils
2import sequtils
3import re
4import tables
5import strformat
6import algorithm
7
8type
9 NodeData = tuple
10 pressure: int
11 neighbors: seq[string]
12
13 Node = tuple
14 id: string
15 data: NodeData
16
17 NodeGraph = Table[string, NodeData]
18
19 ExtendedNodeData = tuple
20 pressure: int
21 neighbors: Table[string, int]
22
23 ExtendedNodeGraph = Table[string, ExtendedNodeData]
24
25proc parseLine(line: string): Node =
26 let tokens = line.replace("Valve ")
27 .replace("has flow rate=")
28 .replace(re"; tunnels? leads? to valves?")
29 .replace(", ", " ")
30 .split(" ")
31 (tokens[0], (tokens[1].parseInt(), @tokens[2..^1]))
32
33proc parseFile(content: string): NodeGraph =
34 let lines = content.strip().splitLines()
35 let nodes = map(lines, parseLine)
36
37 for node in nodes:
38 result[node.id] = node.data
39
40proc bfs(input: NodeGraph, src: string): Table[string, int] =
41 var q = @[(src, 0)]
42 result[src] = 0
43
44 while q.len() > 0:
45 let (curr, pathLen) = q[0]
46 q.delete(0)
47
48 for neighbor in input[curr].neighbors:
49 if result.contains(neighbor):
50 continue
51 result[neighbor] = pathLen+1
52 q.add((neighbor, pathLen+1))
53
54proc setup(input: NodeGraph): ExtendedNodeGraph =
55 for node in input.keys():
56 # Exclude all 0 pressure nodes except the entry node "AA"
57 if (input[node].pressure == 0 and node != "AA"):
58 continue
59 let allConnected = bfs(input, node)
60 var filtConnected: Table[string, int]
61 for conn in allConnected.keys():
62 if input[conn].pressure == 0:
63 continue
64 filtConnected[conn] = allConnected[conn]
65
66 result[node] = (input[node].pressure, filtConnected)
67 # echo fmt"{node} : {result[node]}"
68
69
70var dp: Table[string, int]
71proc normalize(visited: string): string =
72 var s: seq[string]
73 for i in countup(0, visited.len()-1, 2):
74 s.add(visited[i..i+1])
75 result = s.sorted(system.cmp[string]).join()
76
77proc dfs(input: ExtendedNodeGraph, src: string, visited: string, time: int): int =
78 if time <= 1 or visited.len() == input.len():
79 return 0
80
81 # if dp.contains(visited):
82 # return dp[visited]
83
84 let visitedNorm = visited.normalize()
85 let selfScore = (time-1) * input[src].pressure
86 var maxSubscore = 0
87 var maxNeighbor = "undefined"
88 # echo fmt"{src}({selfScore}): "
89 for neighbor in input[src].neighbors.keys():
90 if neighbor in visited:
91 continue
92 let distance = input[src].neighbors[neighbor]
93 let subScore = dfs(input, neighbor, visitedNorm & neighbor, time - 1 - distance)
94 if subScore > maxSubscore:
95 maxSubscore = subScore
96 maxNeighbor = neighbor
97
98 # echo fmt"{src}({selfScore}) => {maxNeighbor}({maxSubscore})"
99 result = selfScore + maxSubscore
100 # dp[visitedNorm & maxNeighbor] = result
101 # echo result
102
103proc solve(input: NodeGraph, time: int): int =
104 let completeGraph = setup(input)
105 # Table of delays to get from starting node "AA" to current
106 let initDelays = completeGraph["AA"].neighbors
107
108 for node in completeGraph.keys():
109 if node == "AA":
110 continue
111 let startTime = time - initDelays[node]
112 result = max(result, dfs(completeGraph, node, node, startTime))
113 echo fmt"{node}({startTime}) : {result}"
114
115let content = readFile("./example.txt")
116
117let input = parseFile(content)
118echo solve(input, 30)