-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
106 lines (98 loc) · 2.65 KB
/
main.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
package main
import (
"fmt"
"slices"
"utils"
)
type Point struct{ x, y int }
type Path []Point
type Distances map[Point]int
type Graph map[Point]Distances
func (point Point) getNeighbours() []Point {
return []Point{
{point.x + 1, point.y}, // >
{point.x - 1, point.y}, // <
{point.x, point.y + 1}, // v
{point.x, point.y - 1}, // ^
}
}
func (point Point) valid(grid []string) bool {
return point.x >= 0 && point.y >= 0 && point.x < len(grid[0]) && point.y < len(grid) && grid[point.y][point.x] != '#'
}
func (path Path) getNext(grid []string, part1 bool) []Path {
head := path[len(path)-1]
if part1 {
symbol := rune(grid[head.y][head.x])
index := slices.Index([]rune{'>', '<', 'v', '^'}, symbol)
if index >= 0 {
next := head.getNeighbours()[index]
if next.valid(grid) && !slices.Contains(path, next) {
return []Path{append(path, head.getNeighbours()[index])}
}
return []Path{}
}
}
paths := []Path{}
for _, next := range head.getNeighbours() {
if next.valid(grid) && !slices.Contains(path, next) {
nextPath := make(Path, len(path))
copy(nextPath, path)
paths = append(paths, append(nextPath, next))
}
}
return paths
}
func search(grid []string, part1 bool) int {
start, finish := Point{1, 0}, Point{len(grid) - 2, len(grid) - 1}
graph := Graph{start: {}}
queue := []Path{{start}}
for len(queue) > 0 { // Create graph of distances between graph vertices
curr := queue[len(queue)-1]
queue = queue[:len(queue)-1]
next := curr.getNext(grid, part1)
vertex := curr[len(curr)-1]
if ((vertex == start || vertex == finish) && len(curr) > 1) || len(next) > 1 { // At a new crossroads or start/end
_, visited := graph[vertex]
if !visited {
graph[vertex] = Distances{}
queue = append(queue, Path{vertex}.getNext(grid, part1)...)
}
graph[curr[0]][vertex] = len(curr) - 1
} else {
queue = append(queue, next...)
}
}
max := 0
queue = []Path{{start}}
for len(queue) > 0 { // Depth-first search on graph of distances
curr := queue[len(queue)-1]
queue = queue[:len(queue)-1]
head := curr[len(curr)-1]
dist, last := graph[head][finish]
if last {
for i := 0; i < len(curr)-1; i++ {
dist += graph[curr[i]][curr[i+1]]
}
if dist > max {
max = dist
}
} else {
for next := range graph[head] {
if !slices.Contains(curr, next) {
nextPath := make(Path, len(curr))
copy(nextPath, curr)
queue = append(queue, append(nextPath, next))
}
}
}
}
return max
}
func solve(grid []string) {
fmt.Println("Part 1:", search(grid, true))
fmt.Println("Part 2:", search(grid, false))
}
func main() {
grid := utils.ReadInput("input.txt", "\n")
utils.TimeFunctionInput(solve, grid)
}