-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmain.go
113 lines (90 loc) · 2.39 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
107
108
109
110
111
112
113
package main
import (
"fmt"
"github.com/believer/aoc-2024/utils/files"
"github.com/believer/aoc-2024/utils/grid"
)
// Brute forced part 2 first by checking every position on the grid.
// It worked, but was slow. We can instead check only the path that
// the guard took during the first run which was more that 78% faster.
// Still over a second, but a lot better. Could most likely be
// parallelized, but I think it's good enough.
func main() {
fmt.Println("Part 1: ", part1("input.txt"))
fmt.Println("Part 2: ", part2("input.txt"))
}
type PointWithDirection struct {
x, y, dx, dy int
}
func part1(name string) int {
lines := files.ReadLines(name)
guardMap := grid.New(lines)
guardLocation := guardMap.Find('^')
return len(getPath(guardMap, guardLocation))
}
func part2(name string) int {
lines := files.ReadLines(name)
guardMap := grid.New(lines)
guardLocation := guardMap.Find('^')
possibleLoops := 0
guardPath := getPath(guardMap, guardLocation)
for p := range guardPath {
if guardMap.Get(p) != '.' {
continue
}
guardMap.Update(p, '#')
if isLoop(guardMap, guardLocation) {
possibleLoops++
}
guardMap.Update(p, '.')
}
return possibleLoops
}
func getPath(guardMap grid.Grid, guard grid.Point) map[grid.Point]bool {
visitedLocations := map[grid.Point]bool{}
dx := 0
dy := -1
for {
x, y := guard.X, guard.Y
visitedLocations[guard] = true
next := grid.Point{X: x + dx, Y: y + dy}
// Check bounds
if _, ok := guardMap.Contains(next); !ok {
break
}
// We always rotate to the right on obstacles
// (-1,0) becomes (0,1) becomes (1,0) becomes (0,-1)
if guardMap.Get(next) == '#' {
dx, dy = -dy, dx
} else {
guard = next
}
}
return visitedLocations
}
func isLoop(guardMap grid.Grid, position grid.Point) bool {
visitedLocations := map[PointWithDirection]bool{}
x, y := position.X, position.Y
dx := 0
dy := -1
for {
// Save with direction as well to find when we're looping
visitedLocations[PointWithDirection{x, y, dx, dy}] = true
next := grid.Point{X: x + dx, Y: y + dy}
// Check bounds
if _, ok := guardMap.Contains(next); !ok {
return false
}
// We always rotate to the right on obstacles
// (-1,0) becomes (0,1) becomes (1,0) becomes (0,-1)
if guardMap.Get(next) == '#' {
dx, dy = -dy, dx
} else {
x += dx
y += dy
}
if _, ok := visitedLocations[PointWithDirection{x, y, dx, dy}]; ok {
return true
}
}
}