diff options
| author | An0nSaiko <porfeas12@gmail.com> | 2022-12-18 23:30:13 +0200 |
|---|---|---|
| committer | An0nSaiko <porfeas12@gmail.com> | 2022-12-18 23:30:13 +0200 |
| commit | 77c9806c3de6ee09130f2c5c2344b4ef054c1433 (patch) | |
| tree | 61e2d2ef5dbdf0cc8253c1389596a946d886701b /day16 | |
| parent | Day 17 (diff) | |
| download | aoc22-77c9806c3de6ee09130f2c5c2344b4ef054c1433.tar.gz aoc22-77c9806c3de6ee09130f2c5c2344b4ef054c1433.zip | |
Day 18
Diffstat (limited to 'day16')
| -rw-r--r-- | day16/solution.nim | 297 |
1 files changed, 155 insertions, 142 deletions
diff --git a/day16/solution.nim b/day16/solution.nim index ae93568..66015d3 100644 --- a/day16/solution.nim +++ b/day16/solution.nim | |||
| @@ -5,25 +5,26 @@ import tables | |||
| 5 | import strformat | 5 | import strformat |
| 6 | import algorithm | 6 | import algorithm |
| 7 | import sets | 7 | import sets |
| 8 | import sugar | ||
| 8 | 9 | ||
| 9 | type | 10 | type |
| 10 | NodeData = tuple | 11 | NodeData = tuple |
| 11 | pressure: int | 12 | pressure: int |
| 12 | neighbors: seq[string] | 13 | neighbors: seq[int64] |
| 13 | 14 | ||
| 14 | Node = tuple | 15 | Node = tuple |
| 15 | id: string | 16 | id: int64 |
| 16 | data: NodeData | 17 | data: NodeData |
| 17 | 18 | ||
| 18 | NodeGraph = Table[string, NodeData] | 19 | NodeGraph = Table[int64, NodeData] |
| 19 | 20 | ||
| 20 | ExtendedNodeData = tuple | 21 | ExtendedNodeData = tuple |
| 21 | pressure: int | 22 | pressure: int |
| 22 | neighbors: Table[string, int] | 23 | neighbors: Table[int64, int] |
| 23 | 24 | ||
| 24 | ExtendedNodeGraph = Table[string, ExtendedNodeData] | 25 | ExtendedNodeGraph = Table[int64, ExtendedNodeData] |
| 25 | 26 | ||
| 26 | proc parseLine(line: string): Node = | 27 | proc parseLine(line: string): auto = |
| 27 | let tokens = line.replace("Valve ") | 28 | let tokens = line.replace("Valve ") |
| 28 | .replace("has flow rate=") | 29 | .replace("has flow rate=") |
| 29 | .replace(re"; tunnels? leads? to valves?") | 30 | .replace(re"; tunnels? leads? to valves?") |
| @@ -35,145 +36,157 @@ proc parseFile(content: string): NodeGraph = | |||
| 35 | let lines = content.strip().splitLines() | 36 | let lines = content.strip().splitLines() |
| 36 | let nodes = map(lines, parseLine) | 37 | let nodes = map(lines, parseLine) |
| 37 | 38 | ||
| 38 | for node in nodes: | 39 | # Create stringID to intID mapping |
| 39 | result[node.id] = node.data | 40 | var nodeIDs: Table[string, int64] |
| 40 | 41 | for i, node in nodes: | |
| 41 | proc bfs(input: NodeGraph, src: string): Table[string, int] = | 42 | nodeIDs[node[0]] = 1 shl i |
| 42 | var q = @[(src, 0)] | 43 | |
| 43 | result[src] = 0 | 44 | echo nodeIDs |
| 44 | 45 | # for i, node in nodes: | |
| 45 | while q.len() > 0: | 46 | # result[nodeIDs[nodes[0]]] = (node[1][0], map(node[1][1], (stringID) => nodeIDs[stringID])) |
| 46 | let (curr, pathLen) = q[0] | 47 | |
| 47 | q.delete(0) | 48 | # proc bfs(input: NodeGraph, src: string): Table[string, int] = |
| 48 | 49 | # var q = @[(src, 0)] | |
| 49 | for neighbor in input[curr].neighbors: | 50 | # result[src] = 0 |
| 50 | if result.contains(neighbor): | 51 | # |
| 51 | continue | 52 | # while q.len() > 0: |
| 52 | result[neighbor] = pathLen+1 | 53 | # let (curr, pathLen) = q[0] |
| 53 | q.add((neighbor, pathLen+1)) | 54 | # q.delete(0) |
| 54 | 55 | # | |
| 55 | proc setup(input: NodeGraph): ExtendedNodeGraph = | 56 | # for neighbor in input[curr].neighbors: |
| 56 | for node in input.keys(): | 57 | # if result.contains(neighbor): |
| 57 | # Exclude all 0 pressure nodes except the entry node "AA" | 58 | # continue |
| 58 | if (input[node].pressure == 0 and node != "AA"): | 59 | # result[neighbor] = pathLen+1 |
| 59 | continue | 60 | # q.add((neighbor, pathLen+1)) |
| 60 | let allConnected = bfs(input, node) | 61 | # |
| 61 | var filtConnected: Table[string, int] | 62 | # proc setup(input: NodeGraph): ExtendedNodeGraph = |
| 62 | for conn in allConnected.keys(): | 63 | # for node in input.keys(): |
| 63 | if input[conn].pressure == 0: | 64 | # # Exclude all 0 pressure nodes except the entry node "AA" |
| 64 | continue | 65 | # if (input[node].pressure == 0 and node != "AA"): |
| 65 | filtConnected[conn] = allConnected[conn] | 66 | # continue |
| 66 | 67 | # let allConnected = bfs(input, node) | |
| 67 | result[node] = (input[node].pressure, filtConnected) | 68 | # var filtConnected: Table[string, int] |
| 68 | # echo fmt"{node} : {result[node]}" | 69 | # for conn in allConnected.keys(): |
| 69 | 70 | # if input[conn].pressure == 0: | |
| 70 | type Agent = tuple | 71 | # continue |
| 71 | valv: string | 72 | # filtConnected[conn] = allConnected[conn] |
| 72 | time: int | 73 | # |
| 73 | 74 | # result[node] = (input[node].pressure, filtConnected) | |
| 74 | # Nodes with their neighbors | 75 | # # echo fmt"{node} : {result[node]}" |
| 75 | var Graph: ExtendedNodeGraph | 76 | # |
| 76 | # Node names | 77 | # type Agent = tuple |
| 77 | var nodes = newSeq[string]() | 78 | # valv: string |
| 78 | 79 | # time: int | |
| 79 | proc playing(agent: Agent, time: int): bool = | 80 | # |
| 80 | agent.time == time | 81 | # # Nodes with their neighbors |
| 81 | 82 | # var Graph: ExtendedNodeGraph | |
| 82 | proc agentScores(agent: Agent, time: int): int = | 83 | # # Node names |
| 83 | Graph[agent.valv].pressure * (time-1) | 84 | # var nodes = newSeq[string]() |
| 84 | 85 | # | |
| 86 | # proc playing(agent: Agent, time: int): bool = | ||
| 87 | # agent.time == time | ||
| 88 | # | ||
| 89 | # proc agentScores(agent: Agent, time: int): int = | ||
| 90 | # Graph[agent.valv].pressure * (time-1) | ||
| 91 | # | ||
| 85 | # type ValveSet = uint64 | 92 | # type ValveSet = uint64 |
| 93 | # | ||
| 86 | # proc incl(set: var ValveSet, valve: uint64): void = | 94 | # proc incl(set: var ValveSet, valve: uint64): void = |
| 87 | # set = set or valve | 95 | # set = set or valve |
| 88 | 96 | # | |
| 89 | var part2 = false | 97 | # proc contains(set: ValveSet, valve: uint64): bool = |
| 90 | proc dfs(visited: HashSet[string], time: int, human, elephant: Agent): int = | 98 | # return (set and valve) > 0 |
| 91 | # echo fmt"time: {time}, human: {human}, elephant: {elephant}" | 99 | # |
| 92 | let remainingValves = nodes.len() - visited.len() | 100 | # var part2 = false |
| 93 | if time <= 1 or remainingValves == 0 or (part2 and human.valv == elephant.valv and remainingValves > 1): | 101 | # proc dfs(visited: ValveSet, time: int, human, elephant: Agent): int = |
| 94 | return 0 | 102 | # # echo fmt"time: {time}, human: {human}, elephant: {elephant}" |
| 95 | 103 | # let remainingValves = nodes.len() - visited.len() | |
| 96 | let | 104 | # if time <= 1 or remainingValves == 0 or (part2 and human.valv == elephant.valv and remainingValves > 1): |
| 97 | neighborsHuman = Graph[human.valv].neighbors | 105 | # return 0 |
| 98 | neighborsElephant = Graph[elephant.valv].neighbors | 106 | # |
| 99 | var | 107 | # let |
| 100 | # check for race | 108 | # neighborsHuman = Graph[human.valv].neighbors |
| 101 | humanScore = if human.valv notin visited: human.agentScores(time) else: 0 | 109 | # neighborsElephant = Graph[elephant.valv].neighbors |
| 102 | elephantScore = if elephant.valv notin visited: elephant.agentScores(time) else: 0 | 110 | # var |
| 103 | 111 | # # check for race | |
| 104 | var bestNextScore = 0 | 112 | # humanScore = if human.valv notin visited: human.agentScores(time) else: 0 |
| 105 | if human.playing(time) and elephant.playing(time): | 113 | # elephantScore = if elephant.valv notin visited: elephant.agentScores(time) else: 0 |
| 106 | for nextHuman in nodes: | 114 | # |
| 107 | for nextElephant in nodes: | 115 | # var bestNextScore = 0 |
| 108 | if nextHuman in visited or nextElephant in visited: | 116 | # if human.playing(time) and elephant.playing(time): |
| 109 | continue | 117 | # for nextHuman in nodes: |
| 110 | let | 118 | # for nextElephant in nodes: |
| 111 | nextTimeHuman = time - 1 - neighborsHuman[nextHuman] | 119 | # if nextHuman in visited or nextElephant in visited: |
| 112 | nextTimeElephant = time - 1 - neighborsElephant[nextElephant] | 120 | # continue |
| 113 | nextTime = max(nextTimeHuman, nextTimeElephant) | 121 | # let |
| 114 | nextScore = dfs(union(visited, toHashSet(@[elephant.valv, human.valv])), nextTime, (nextHuman, nextTimeHuman), (nextElephant, nextTimeElephant)) | 122 | # nextTimeHuman = time - 1 - neighborsHuman[nextHuman] |
| 115 | bestNextScore = max(bestNextScore, nextScore) | 123 | # nextTimeElephant = time - 1 - neighborsElephant[nextElephant] |
| 116 | 124 | # nextTime = max(nextTimeHuman, nextTimeElephant) | |
| 117 | # elephant keeps his score only if he isn't in the same valve as human | 125 | # nextScore = dfs(union(visited, toHashSet(@[elephant.valv, human.valv])), nextTime, (nextHuman, nextTimeHuman), (nextElephant, nextTimeElephant)) |
| 118 | elephantScore = (if human != elephant: elephantScore else: 0) | 126 | # bestNextScore = max(bestNextScore, nextScore) |
| 119 | return bestNextScore + humanScore + elephantScore | 127 | # |
| 120 | 128 | # # elephant keeps his score only if he isn't in the same valve as human | |
| 121 | if human.playing(time): | 129 | # elephantScore = (if human != elephant: elephantScore else: 0) |
| 122 | for nextHuman in nodes: | 130 | # return bestNextScore + humanScore + elephantScore |
| 123 | if nextHuman in visited: | 131 | # |
| 124 | continue | 132 | # if human.playing(time): |
| 125 | let | 133 | # for nextHuman in nodes: |
| 126 | nextTimeHuman = time - 1 - neighborsHuman[nextHuman] | 134 | # if nextHuman in visited: |
| 127 | nextTime = max(nextTimeHuman, elephant.time) | 135 | # continue |
| 128 | nextScore = dfs(union(visited, toHashSet(@[human.valv])), nextTime, (nextHuman, nextTimeHuman), elephant) | 136 | # let |
| 129 | bestNextScore = max(bestNextScore, nextScore) | 137 | # nextTimeHuman = time - 1 - neighborsHuman[nextHuman] |
| 130 | 138 | # nextTime = max(nextTimeHuman, elephant.time) | |
| 131 | # Check for race | 139 | # nextScore = dfs(union(visited, toHashSet(@[human.valv])), nextTime, (nextHuman, nextTimeHuman), elephant) |
| 132 | return bestNextScore + humanScore | 140 | # bestNextScore = max(bestNextScore, nextScore) |
| 133 | 141 | # | |
| 134 | if elephant.playing(time): | 142 | # # Check for race |
| 135 | for nextElephant in nodes: | 143 | # return bestNextScore + humanScore |
| 136 | if nextElephant in visited: | 144 | # |
| 137 | continue | 145 | # if elephant.playing(time): |
| 138 | let | 146 | # for nextElephant in nodes: |
| 139 | nextTimeElephant = time - 1 - neighborsElephant[nextElephant] | 147 | # if nextElephant in visited: |
| 140 | nextTime = max(human.time, nextTimeElephant) | 148 | # continue |
| 141 | nextScore = dfs(union(visited, toHashSet(@[elephant.valv])), nextTime, human, (nextElephant, nextTimeElephant)) | 149 | # let |
| 142 | bestNextScore = max(bestNextScore, nextScore) | 150 | # nextTimeElephant = time - 1 - neighborsElephant[nextElephant] |
| 143 | 151 | # nextTime = max(human.time, nextTimeElephant) | |
| 144 | # Check for race | 152 | # nextScore = dfs(union(visited, toHashSet(@[elephant.valv])), nextTime, human, (nextElephant, nextTimeElephant)) |
| 145 | return bestNextScore + elephantScore | 153 | # bestNextScore = max(bestNextScore, nextScore) |
| 146 | 154 | # | |
| 147 | assert(false) | 155 | # # Check for race |
| 148 | 156 | # return bestNextScore + elephantScore | |
| 149 | proc solve(initDelays: Table[string, int], time: int): int = | 157 | # |
| 150 | 158 | # assert(false) | |
| 151 | if not part2: | 159 | # |
| 152 | for human in nodes: | 160 | # proc solve(initDelays: Table[string, int], time: int): int = |
| 153 | let | 161 | # |
| 154 | humanStartTime = time - initDelays[human] | 162 | # if not part2: |
| 155 | startTime = humanStartTime | 163 | # for human in nodes: |
| 156 | result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (human, -1))) | 164 | # let |
| 157 | else: | 165 | # humanStartTime = time - initDelays[human] |
| 158 | for i, human in nodes[0 ..< ^1]: | 166 | # startTime = humanStartTime |
| 159 | for elephant in nodes[i+1 .. ^1]: | 167 | # result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (human, -1))) |
| 160 | let | 168 | # else: |
| 161 | elephantStartTime = time - initDelays[elephant] | 169 | # for i, human in nodes[0 ..< ^1]: |
| 162 | humanStartTime = time - initDelays[human] | 170 | # for elephant in nodes[i+1 .. ^1]: |
| 163 | startTime = max(elephantStartTime, humanStartTime) | 171 | # let |
| 164 | result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (elephant, elephantStartTime))) | 172 | # elephantStartTime = time - initDelays[elephant] |
| 173 | # humanStartTime = time - initDelays[human] | ||
| 174 | # startTime = max(elephantStartTime, humanStartTime) | ||
| 175 | # result = max(result, dfs(initHashSet[string](), startTime, (human, humanStartTime), (elephant, elephantStartTime))) | ||
| 176 | # | ||
| 177 | # | ||
| 165 | 178 | ||
| 166 | let content = readFile("./example.txt") | 179 | let content = readFile("./example.txt") |
| 167 | |||
| 168 | let input = parseFile(content) | 180 | let input = parseFile(content) |
| 169 | Graph = setup(input) | 181 | echo input |
| 170 | # Table of delays to get from starting node "AA" to current | 182 | |
| 171 | let initDelays = Graph["AA"].neighbors | 183 | # # Table of delays to get from starting node "AA" to current |
| 172 | Graph.del("AA") | 184 | # let initDelays = Graph["AA"].neighbors |
| 173 | 185 | # Graph.del("AA") | |
| 174 | for node in Graph.keys(): | 186 | # |
| 175 | nodes.add(node) | 187 | # for node in Graph.keys(): |
| 176 | 188 | # nodes.add(node) | |
| 177 | echo solve(initDelays, 30) | 189 | # |
| 178 | part2 = true | 190 | # echo solve(initDelays, 30) |
| 179 | echo solve(initDelays, 26) | 191 | # part2 = true |
| 192 | # echo solve(initDelays, 26) | ||
