diff options
| author | An0nSaiko <porfeas12@gmail.com> | 2022-12-17 09:36:15 +0200 |
|---|---|---|
| committer | An0nSaiko <porfeas12@gmail.com> | 2022-12-17 09:36:15 +0200 |
| commit | bdd738d805a7008382ae26460bc12523ef2d4a3e (patch) | |
| tree | eb862e2e7678548104d288068f038109fc5f5452 | |
| parent | Day 16 (part1) (diff) | |
| download | aoc22-bdd738d805a7008382ae26460bc12523ef2d4a3e.tar.gz aoc22-bdd738d805a7008382ae26460bc12523ef2d4a3e.zip | |
Day 16 (part1 & part2). Part2 is too slow and only works for the example
| -rw-r--r-- | day16/solution.nim | 145 |
1 files changed, 103 insertions, 42 deletions
diff --git a/day16/solution.nim b/day16/solution.nim index b6d39e6..ae93568 100644 --- a/day16/solution.nim +++ b/day16/solution.nim | |||
| @@ -4,6 +4,7 @@ import re | |||
| 4 | import tables | 4 | import tables |
| 5 | import strformat | 5 | import strformat |
| 6 | import algorithm | 6 | import algorithm |
| 7 | import sets | ||
| 7 | 8 | ||
| 8 | type | 9 | type |
| 9 | NodeData = tuple | 10 | NodeData = tuple |
| @@ -66,53 +67,113 @@ proc setup(input: NodeGraph): ExtendedNodeGraph = | |||
| 66 | result[node] = (input[node].pressure, filtConnected) | 67 | result[node] = (input[node].pressure, filtConnected) |
| 67 | # echo fmt"{node} : {result[node]}" | 68 | # echo fmt"{node} : {result[node]}" |
| 68 | 69 | ||
| 70 | type Agent = tuple | ||
| 71 | valv: string | ||
| 72 | time: int | ||
| 69 | 73 | ||
| 70 | var dp: Table[string, int] | 74 | # Nodes with their neighbors |
| 71 | proc normalize(visited: string): string = | 75 | var Graph: ExtendedNodeGraph |
| 72 | var s: seq[string] | 76 | # Node names |
| 73 | for i in countup(0, visited.len()-1, 2): | 77 | var nodes = newSeq[string]() |
| 74 | s.add(visited[i..i+1]) | ||
| 75 | result = s.sorted(system.cmp[string]).join() | ||
| 76 | 78 | ||
| 77 | proc dfs(input: ExtendedNodeGraph, src: string, visited: string, time: int): int = | 79 | proc playing(agent: Agent, time: int): bool = |
| 78 | if time <= 1 or visited.len() == input.len(): | 80 | agent.time == time |
| 79 | return 0 | ||
| 80 | 81 | ||
| 81 | # if dp.contains(visited): | 82 | proc agentScores(agent: Agent, time: int): int = |
| 82 | # return dp[visited] | 83 | Graph[agent.valv].pressure * (time-1) |
| 83 | 84 | ||
| 84 | let visitedNorm = visited.normalize() | 85 | # type ValveSet = uint64 |
| 85 | let selfScore = (time-1) * input[src].pressure | 86 | # proc incl(set: var ValveSet, valve: uint64): void = |
| 86 | var maxSubscore = 0 | 87 | # set = set or valve |
| 87 | var maxNeighbor = "undefined" | 88 | |
| 88 | # echo fmt"{src}({selfScore}): " | 89 | var part2 = false |
| 89 | for neighbor in input[src].neighbors.keys(): | 90 | proc dfs(visited: HashSet[string], time: int, human, elephant: Agent): int = |
| 90 | if neighbor in visited: | 91 | # echo fmt"time: {time}, human: {human}, elephant: {elephant}" |
| 91 | continue | 92 | let remainingValves = nodes.len() - visited.len() |
| 92 | let distance = input[src].neighbors[neighbor] | 93 | if time <= 1 or remainingValves == 0 or (part2 and human.valv == elephant.valv and remainingValves > 1): |
| 93 | let subScore = dfs(input, neighbor, visitedNorm & neighbor, time - 1 - distance) | 94 | return 0 |
| 94 | if subScore > maxSubscore: | 95 | |
| 95 | maxSubscore = subScore | 96 | let |
| 96 | maxNeighbor = neighbor | 97 | neighborsHuman = Graph[human.valv].neighbors |
| 97 | 98 | neighborsElephant = Graph[elephant.valv].neighbors | |
| 98 | # echo fmt"{src}({selfScore}) => {maxNeighbor}({maxSubscore})" | 99 | var |
| 99 | result = selfScore + maxSubscore | 100 | # check for race |
| 100 | # dp[visitedNorm & maxNeighbor] = result | 101 | humanScore = if human.valv notin visited: human.agentScores(time) else: 0 |
| 101 | # echo result | 102 | elephantScore = if elephant.valv notin visited: elephant.agentScores(time) else: 0 |
| 102 | 103 | ||
| 103 | proc solve(input: NodeGraph, time: int): int = | 104 | var bestNextScore = 0 |
| 104 | let completeGraph = setup(input) | 105 | if human.playing(time) and elephant.playing(time): |
| 105 | # Table of delays to get from starting node "AA" to current | 106 | for nextHuman in nodes: |
| 106 | let initDelays = completeGraph["AA"].neighbors | 107 | for nextElephant in nodes: |
| 107 | 108 | if nextHuman in visited or nextElephant in visited: | |
| 108 | for node in completeGraph.keys(): | 109 | continue |
| 109 | if node == "AA": | 110 | let |
| 110 | continue | 111 | nextTimeHuman = time - 1 - neighborsHuman[nextHuman] |
| 111 | let startTime = time - initDelays[node] | 112 | nextTimeElephant = time - 1 - neighborsElephant[nextElephant] |
| 112 | result = max(result, dfs(completeGraph, node, node, startTime)) | 113 | nextTime = max(nextTimeHuman, nextTimeElephant) |
| 113 | echo fmt"{node}({startTime}) : {result}" | 114 | nextScore = dfs(union(visited, toHashSet(@[elephant.valv, human.valv])), nextTime, (nextHuman, nextTimeHuman), (nextElephant, nextTimeElephant)) |
| 115 | bestNextScore = max(bestNextScore, nextScore) | ||
| 116 | |||
| 117 | # elephant keeps his score only if he isn't in the same valve as human | ||
| 118 | elephantScore = (if human != elephant: elephantScore else: 0) | ||
| 119 | return bestNextScore + humanScore + elephantScore | ||
| 120 | |||
| 121 | if human.playing(time): | ||
| 122 | for nextHuman in nodes: | ||
| 123 | if nextHuman in visited: | ||
| 124 | continue | ||
| 125 | let | ||
| 126 | nextTimeHuman = time - 1 - neighborsHuman[nextHuman] | ||
| 127 | nextTime = max(nextTimeHuman, elephant.time) | ||
| 128 | nextScore = dfs(union(visited, toHashSet(@[human.valv])), nextTime, (nextHuman, nextTimeHuman), elephant) | ||
| 129 | bestNextScore = max(bestNextScore, nextScore) | ||
| 130 | |||
| 131 | # Check for race | ||
| 132 | return bestNextScore + humanScore | ||
| 133 | |||
| 134 | if elephant.playing(time): | ||
| 135 | for nextElephant in nodes: | ||
| 136 | if nextElephant in visited: | ||
| 137 | continue | ||
| 138 | let | ||
| 139 | nextTimeElephant = time - 1 - neighborsElephant[nextElephant] | ||
| 140 | nextTime = max(human.time, nextTimeElephant) | ||
| 141 | nextScore = dfs(union(visited, toHashSet(@[elephant.valv])), nextTime, human, (nextElephant, nextTimeElephant)) | ||
| 142 | bestNextScore = max(bestNextScore, nextScore) | ||
| 143 | |||
| 144 | # Check for race | ||
| 145 | return bestNextScore + elephantScore | ||
| 146 | |||
| 147 | assert(false) | ||
| 148 | |||
| 149 | proc solve(initDelays: Table[string, int], time: int): int = | ||
| 150 | |||
| 151 | if not part2: | ||
| 152 | for human in nodes: | ||
| 153 | let | ||
| 154 | humanStartTime = time - initDelays[human] | ||
| 155 | startTime = humanStartTime | ||
| 156 | result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (human, -1))) | ||
| 157 | else: | ||
| 158 | for i, human in nodes[0 ..< ^1]: | ||
| 159 | for elephant in nodes[i+1 .. ^1]: | ||
| 160 | let | ||
| 161 | elephantStartTime = time - initDelays[elephant] | ||
| 162 | humanStartTime = time - initDelays[human] | ||
| 163 | startTime = max(elephantStartTime, humanStartTime) | ||
| 164 | result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (elephant, elephantStartTime))) | ||
| 114 | 165 | ||
| 115 | let content = readFile("./example.txt") | 166 | let content = readFile("./example.txt") |
| 116 | 167 | ||
| 117 | let input = parseFile(content) | 168 | let input = parseFile(content) |
| 118 | echo solve(input, 30) | 169 | Graph = setup(input) |
| 170 | # Table of delays to get from starting node "AA" to current | ||
| 171 | let initDelays = Graph["AA"].neighbors | ||
| 172 | Graph.del("AA") | ||
| 173 | |||
| 174 | for node in Graph.keys(): | ||
| 175 | nodes.add(node) | ||
| 176 | |||
| 177 | echo solve(initDelays, 30) | ||
| 178 | part2 = true | ||
| 179 | echo solve(initDelays, 26) | ||
