@@ -6,6 +6,10 @@ import (
6
6
"github.com/believer/aoc-2024/utils/files"
7
7
)
8
8
9
+ // Brute forced part 2 first by checking every position on the grid.
10
+ // It worked, but was slow. We can instead check only the path that
11
+ // the guard took during the first run which was more that 78% faster.
12
+ // Still over a second, but a lot better.
9
13
func main () {
10
14
fmt .Println ("Part 1: " , part1 ("input.txt" ))
11
15
fmt .Println ("Part 2: " , part2 ("input.txt" ))
@@ -15,31 +19,76 @@ type Position struct {
15
19
r , c int
16
20
}
17
21
22
+ type PositionWithDirection struct {
23
+ r , c , dr , dc int
24
+ }
25
+
18
26
func part1 (name string ) int {
19
27
lines := files .ReadLines (name )
20
- visitedLocations := make (map [Position ]struct {})
28
+ guardLocation := initialGuardLocation (lines )
29
+
30
+ return len (getPath (lines , guardLocation ))
31
+ }
32
+
33
+ func part2 (name string ) int {
34
+ lines := files .ReadLines (name )
35
+ possibleLoops := 0
36
+ guardLocation := initialGuardLocation (lines )
37
+
38
+ // Create a grid to modify
39
+ var grid [][]byte
40
+
41
+ for _ , l := range lines {
42
+ grid = append (grid , []byte (l ))
43
+ }
44
+
45
+ guardPath := getPath (lines , guardLocation )
21
46
47
+ for p := range guardPath {
48
+ if lines [p.r ][p.c ] != '.' {
49
+ continue
50
+ }
51
+
52
+ grid [p.r ][p.c ] = '#'
53
+
54
+ if isLoop (grid , guardLocation .r , guardLocation .c ) {
55
+ possibleLoops ++
56
+ }
57
+
58
+ grid [p.r ][p.c ] = '.'
59
+ }
60
+
61
+ return possibleLoops
62
+ }
63
+
64
+ func initialGuardLocation (lines []string ) Position {
65
+ guardLocation := Position {0 , 0 }
22
66
rows := len (lines )
23
67
cols := len (lines [0 ])
24
- guardLocation := Position {0 , 0 }
25
68
26
- // Find initial guard location
27
69
outer:
28
70
for r := range rows {
29
71
for c := range cols {
30
- if string ( lines [r ][c ]) == "^" {
72
+ if lines [r ][c ] == '^' {
31
73
guardLocation = Position {r , c }
32
74
break outer
33
75
}
34
76
}
35
77
}
36
78
79
+ return guardLocation
80
+ }
81
+
82
+ func getPath (lines []string , guard Position ) map [Position ]struct {} {
83
+ visitedLocations := make (map [Position ]struct {})
84
+ rows := len (lines )
85
+ cols := len (lines [0 ])
37
86
dr := - 1
38
87
dc := 0
39
88
40
89
for {
41
- r , c := guardLocation .r , guardLocation .c
42
- visitedLocations [guardLocation ] = struct {}{}
90
+ r , c := guard .r , guard .c
91
+ visitedLocations [guard ] = struct {}{}
43
92
44
93
// Check bounds
45
94
if r + dr < 0 || r + dr >= rows || c + dc < 0 || c + dc >= cols {
@@ -48,16 +97,43 @@ outer:
48
97
49
98
// We always rotate to the right on obstacles
50
99
// (-1,0) becomes (0,1) becomes (1,0) becomes (0,-1)
51
- if string ( lines [r + dr ][c + dc ]) == "#" {
100
+ if lines [r + dr ][c + dc ] == '#' {
52
101
dc , dr = - dr , dc
53
102
} else {
54
- guardLocation = Position {r : r + dr , c : c + dc }
103
+ guard = Position {r : r + dr , c : c + dc }
55
104
}
56
105
}
57
106
58
- return len ( visitedLocations )
107
+ return visitedLocations
59
108
}
60
109
61
- func part2 (name string ) int {
62
- return 0
110
+ func isLoop (grid [][]byte , r , c int ) bool {
111
+ visitedLocations := make (map [PositionWithDirection ]struct {})
112
+ dr := - 1
113
+ dc := 0
114
+ rows := len (grid )
115
+ cols := len (grid [0 ])
116
+
117
+ for {
118
+ // Save with direction as well to find when we're looping
119
+ visitedLocations [PositionWithDirection {r , c , dr , dc }] = struct {}{}
120
+
121
+ // Check bounds
122
+ if r + dr < 0 || r + dr >= rows || c + dc < 0 || c + dc >= cols {
123
+ return false
124
+ }
125
+
126
+ // We always rotate to the right on obstacles
127
+ // (-1,0) becomes (0,1) becomes (1,0) becomes (0,-1)
128
+ if grid [r + dr ][c + dc ] == '#' {
129
+ dc , dr = - dr , dc
130
+ } else {
131
+ r += dr
132
+ c += dc
133
+ }
134
+
135
+ if _ , ok := visitedLocations [PositionWithDirection {r , c , dr , dc }]; ok {
136
+ return true
137
+ }
138
+ }
63
139
}
0 commit comments