-
Notifications
You must be signed in to change notification settings - Fork 0
/
day16.go
244 lines (215 loc) · 6.22 KB
/
day16.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"time"
)
func parseValves(lines []string) map[string]Valve {
// this function parses each line
// Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
m := make(map[string]Valve)
for _, line := range lines {
words := strings.Split(line, " ")
valveName := words[1]
r := strings.Split(words[4], "=")[1]
rate, err := strconv.Atoi(strings.TrimRight(r, ";"))
if err != nil {
panic("Could not convert")
}
va := strings.Join(words[9:], " ")
valves := strings.FieldsFunc(va, func(r rune) bool {
if r == ',' || r == ' ' {
return true
}
return false
})
v := NewValve(rate, valves...)
m[valveName] = v
}
return m
}
func BuildMatrix(m *map[string]Valve) map[string]map[string]int {
// this function returns a matrix of all the "quickest routes"
// to each destination using the Floyd Warshall Algorithm.
matrix := make(map[string]map[string]int, 0)
inf := 99
for k := range *m {
matrix[k] = make(map[string]int)
for j := range *m {
if k == j {
matrix[k][j] = 0
} else {
matrix[k][j] = inf
}
}
}
// at this point, the matrix is completed and can be reference with
// matrix["from"]["to"]. Now work on the edges that we know of.
for k, v := range *m {
for _, tunnel := range v.tunnels {
matrix[k][tunnel] = 1
}
}
// edges are built at this point. Now IN THEORY we can build a new
// matrix out of this
for k := range matrix {
for i := range matrix {
for j := range matrix {
if matrix[i][j] > matrix[i][k]+matrix[k][j] {
matrix[i][j] = matrix[i][k] + matrix[k][j]
}
}
}
}
return matrix
}
func PrintMatrix(matrix *map[string]map[string]int) {
// this function simply prints the contents of the matrix.
// NOTE: This function is wonky. It works kinda, but it
// never matches up due to the un-ordered nature of maps in Go.
// So it looks pretty but it's wrong, though it can be fixed if
// I cared enough.
fmt.Printf(" ")
for k := range *matrix {
fmt.Printf("%s ", k)
}
fmt.Printf("\n")
for k, v := range *matrix {
fmt.Printf("%s: ", k)
for _, w := range v {
fmt.Printf(" %02d ", w)
}
fmt.Printf("\n")
}
}
func bestChoice(current string, minutes int, matrix map[string]map[string]int, m *map[string]Valve) string {
// this function will take the current position, the current amount of minutes
// left, the built matrix, and the full map to determine the next place we
// should be moving. This will NOT do the proper path-finding, if that is even
// necessary. It will simply return the next place we should be moving to.
//
// NOTE: this was the wrong way of going about doing this. This function was
// unused but I kept it anyway
var choice string
var highestFlow int = 0
for k, v := range *m {
if v.isOpen {
// valve is already open, ignore
continue
}
// get the distance
distance := matrix[current][k]
totalFlow := ((minutes - distance) - 1) * v.flowRate // additional -1 to open the valve once we're there
if totalFlow > highestFlow {
highestFlow = totalFlow
choice = k
fmt.Printf("Highest so far: %s with a total flow of %d and a distance of %d\n", choice, highestFlow, distance)
}
}
return choice
}
func decideValves(paths []string, m map[string]Valve) (string, int) {
// given the superset of valves as m, and which valve
// we are currently have access to get to, this looks
// at each flowrate of the given valves against the m
// superset and decides which valve offers the best
// path _as well as_ the flowrate.
bestRate := 0
bestValve := ""
for _, v := range paths {
if m[v].flowRate > bestRate && !m[v].isOpen {
bestRate = m[v].flowRate
bestValve = v
}
}
return bestValve, bestRate
}
// depth first search
func DFS(current string, minutes int, matrix map[string]map[string]int, thisPath Path, m map[string]Valve) []Path {
var p []Path
for k, v := range m {
distance := minutes - matrix[current][k] - 1
if distance <= 0 || thisPath.BeenThere(k) || v.flowRate == 0 {
continue
// we've been there or it's way too far or it's just not worth it
}
thisFlow := v.flowRate * distance
// copy the path, this screwed me up before I realized this was necessary!
newPath := thisPath.CopyPath()
// otherwise, we haven't been there, so let's see what happens
newPath.AddToPath(k, thisFlow)
p = append(p, DFS(k, distance, matrix, newPath, m)...)
}
p = append(p, thisPath)
return p
}
func FindTwoHighest(pathList []Path) (Path, Path) {
// first, find the unique pairs. 2-dimensional array the holds the
// index of the unique pairs
uniquePairs := make([][2]int, 0)
for o, outer := range pathList {
for i, inner := range pathList {
unique := true
for k := range outer.breadCrumbs.location {
if inner.breadCrumbs.Contains(k) {
// this is not unique
unique = false
}
}
if unique {
var pair [2]int
pair[0] = o
pair[1] = i
uniquePairs = append(uniquePairs, pair)
}
}
}
var highest int = -1
var topScore int = 0
var combinedScore int = 0
for i, v := range uniquePairs {
combinedScore = pathList[v[0]].totalPressure + pathList[v[1]].totalPressure
if combinedScore > topScore {
topScore = combinedScore
highest = i
}
}
return pathList[uniquePairs[highest][0]], pathList[uniquePairs[highest][1]]
}
func main() {
readFile, err := os.Open("input")
if err != nil {
fmt.Println(err)
}
fileScanner := bufio.NewScanner(readFile)
fileScanner.Split(bufio.ScanLines)
var lines []string
for fileScanner.Scan() {
lines = append(lines, fileScanner.Text())
}
readFile.Close()
// time this
t1 := time.Now()
// parse the valves from the input
m := parseValves(lines)
// part 1, first build the matrix
matrix := BuildMatrix(&m)
paths := DFS("AA", 30, matrix, NewPath(), m)
highest := 0
for _, v := range paths {
if v.totalPressure > highest {
highest = v.totalPressure
}
}
fmt.Printf("Highest from Part 1: %d\n", highest)
fmt.Printf("Time elapsed: %s\n", time.Since(t1))
// Now for part 2.
t2 := time.Now()
pathsPartTwo := DFS("AA", 26, matrix, NewPath(), m)
t, s := FindTwoHighest(pathsPartTwo)
fmt.Printf("First: %d, Second: %d, Total: %d\n", t.totalPressure, s.totalPressure, s.totalPressure+t.totalPressure)
fmt.Printf("Time elapsed: %s\n", time.Since(t2))
}