Skip to content

Commit dc9cd1f

Browse files
committed
2023 day08 optimised
1 parent f7693ab commit dc9cd1f

File tree

5 files changed

+106
-44
lines changed

5 files changed

+106
-44
lines changed

2023/day08/.bench

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"Lines":[{"Name":"Part 1","N":1327,"NsPerOp":911496,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":220,"NsPerOp":5524126,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}
1+
{"Lines":[{"Name":"Part 1","N":4512,"NsPerOp":263697,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":738,"NsPerOp":1584917,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}

2023/day08/main.go

Lines changed: 64 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package main
22

33
import (
4-
"aocli/utils"
54
_ "embed"
65
"os"
7-
"strings"
86
)
97

108
//go:embed input.txt
@@ -26,51 +24,76 @@ func main() {
2624
println(answer)
2725
}
2826

29-
type Maps map[rune]map[string]string
27+
type Node struct {
28+
left string
29+
right string
30+
}
3031

31-
func solve(input string, suffix string) int {
32-
lines := strings.Split(strings.TrimSpace(input), "\n\n")
33-
instr := []rune(lines[0])
34-
maps := buildMaps(lines[1])
35-
P := []string{}
36-
for p := range maps['L'] {
37-
if strings.HasSuffix(p, suffix) {
38-
P = append(P, p)
39-
}
40-
}
32+
var (
33+
instructions string
34+
network map[string]Node
35+
startNodes []string
36+
)
4137

42-
I := []int{}
38+
func parseInput(input string) {
4339
i := 0
44-
for {
45-
NP := []string{}
46-
for _, p := range P {
47-
p = maps[instr[i%len(instr)]][p]
48-
if strings.HasSuffix(p, "Z") {
49-
I = append(I, i+1)
50-
if len(I) == len(P) {
51-
return utils.LCM(1, I[0], I[1:]...)
52-
}
53-
}
54-
NP = append(NP, p)
55-
}
56-
P = NP
40+
// Skip whitespace
41+
for i < len(input) && (input[i] == ' ' || input[i] == '\n' || input[i] == '\r') {
42+
i++
43+
}
44+
45+
// Parse instructions
46+
start := i
47+
for i < len(input) && input[i] != '\n' {
48+
i++
49+
}
50+
instructions = input[start:i]
51+
52+
// Skip blank lines
53+
for i < len(input) && (input[i] == '\n' || input[i] == '\r') {
5754
i++
5855
}
56+
57+
// Parse network
58+
network = make(map[string]Node)
59+
startNodes = make([]string, 0, 6)
60+
61+
for i < len(input) {
62+
if input[i] == '\n' || input[i] == '\r' {
63+
i++
64+
continue
65+
}
66+
67+
// Parse: "AAA = (BBB, CCC)"
68+
name := input[i : i+3]
69+
i += 7 // Skip " = ("
70+
left := input[i : i+3]
71+
i += 5 // Skip ", "
72+
right := input[i : i+3]
73+
i += 4 // Skip ")\n"
74+
75+
network[name] = Node{left: left, right: right}
76+
if name[2] == 'A' {
77+
startNodes = append(startNodes, name)
78+
}
79+
}
80+
}
81+
82+
func gcd(a, b int) int {
83+
for b != 0 {
84+
a, b = b, a%b
85+
}
86+
return a
87+
}
88+
89+
func lcm(a, b int) int {
90+
return a * b / gcd(a, b)
5991
}
6092

61-
func buildMaps(line string) Maps {
62-
lines := strings.Split(line, "\n")
63-
maps := make(map[rune]map[string]string, 2)
64-
maps['L'] = make(map[string]string, len(lines))
65-
maps['R'] = make(map[string]string, len(lines))
66-
for _, line := range lines {
67-
s := strings.Split(line, " = ")
68-
I := s[0]
69-
lr := strings.Split(s[1], ", ")
70-
L := lr[0][1:]
71-
R := lr[1][:3]
72-
maps['L'][I] = L
73-
maps['R'][I] = R
93+
func lcmSlice(nums []int) int {
94+
result := nums[0]
95+
for i := 1; i < len(nums); i++ {
96+
result = lcm(result, nums[i])
7497
}
75-
return maps
98+
return result
7699
}

2023/day08/main_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ package main
33
import "testing"
44

55
func BenchmarkPartOne(b *testing.B) {
6+
parseInput(input)
7+
b.ResetTimer()
68
for n := 0; n < b.N; n++ {
79
doPartOne(input)
810
}
911
}
1012

1113
func BenchmarkPartTwo(b *testing.B) {
14+
parseInput(input)
15+
b.ResetTimer()
1216
for n := 0; n < b.N; n++ {
1317
doPartTwo(input)
1418
}

2023/day08/part1.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
package main
22

33
func doPartOne(input string) int {
4-
return solve(input, "AAA")
4+
current := "AAA"
5+
steps := 0
6+
instrLen := len(instructions)
7+
8+
for current != "ZZZ" {
9+
node := network[current]
10+
if instructions[steps%instrLen] == 'L' {
11+
current = node.left
12+
} else {
13+
current = node.right
14+
}
15+
steps++
16+
}
17+
18+
return steps
519
}

2023/day08/part2.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
package main
22

33
func doPartTwo(input string) int {
4-
return solve(input, "A")
4+
// Find cycle length for each starting node
5+
cycles := make([]int, 0, len(startNodes))
6+
instrLen := len(instructions)
7+
8+
for _, start := range startNodes {
9+
current := start
10+
steps := 0
11+
12+
for current[2] != 'Z' {
13+
node := network[current]
14+
if instructions[steps%instrLen] == 'L' {
15+
current = node.left
16+
} else {
17+
current = node.right
18+
}
19+
steps++
20+
}
21+
22+
cycles = append(cycles, steps)
23+
}
24+
25+
return lcmSlice(cycles)
526
}

0 commit comments

Comments
 (0)