Skip to content

Commit f10b8d2

Browse files
committed
Solve Day 8
Signed-off-by: Alexander Kurbatov <sir.alkurbatov@yandex.ru>
1 parent 4034b24 commit f10b8d2

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

2023/8/main.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// --- Day 8: Haunted Wasteland ---
2+
// https://adventofcode.com/2023/day/8
3+
package main
4+
5+
import (
6+
"bufio"
7+
"errors"
8+
"log"
9+
"os"
10+
"regexp"
11+
"strings"
12+
)
13+
14+
var errNoNodes = errors.New("no nodes found")
15+
16+
var nodeRe = regexp.MustCompile(`[\dA-Z]{3}`)
17+
18+
type Node struct {
19+
id string
20+
left string
21+
right string
22+
}
23+
24+
func parseRoute(scanner *bufio.Scanner) ([]rune, error) {
25+
scanner.Scan()
26+
27+
if err := scanner.Err(); err != nil {
28+
return nil, err
29+
}
30+
31+
return []rune(scanner.Text()), nil
32+
}
33+
34+
func parseNodes(scanner *bufio.Scanner) (map[string]Node, error) {
35+
nodes := make(map[string]Node, 0)
36+
37+
for scanner.Scan() {
38+
src := scanner.Text()
39+
40+
if src == "" {
41+
continue
42+
}
43+
44+
fields := nodeRe.FindAllString(src, -1)
45+
if fields == nil || len(fields) < 3 {
46+
return nil, errNoNodes
47+
}
48+
49+
nodes[fields[0]] = Node{fields[0], fields[1], fields[2]}
50+
}
51+
52+
if err := scanner.Err(); err != nil {
53+
log.Printf("Failed to scan seeds from input.txt: %v", err)
54+
return nil, err
55+
}
56+
57+
if len(nodes) == 0 {
58+
return nil, errNoNodes
59+
}
60+
61+
return nodes, nil
62+
}
63+
64+
func calcSteps(route []rune, from, to string, nodes map[string]Node) int {
65+
var steps, pos int
66+
67+
for {
68+
if strings.HasSuffix(nodes[from].id, to) {
69+
return steps
70+
}
71+
72+
direction := route[pos]
73+
if direction == 'L' {
74+
from = nodes[from].left
75+
} else {
76+
from = nodes[from].right
77+
}
78+
79+
pos++
80+
if pos == len(route) {
81+
pos = 0
82+
}
83+
84+
steps++
85+
}
86+
}
87+
88+
// Find greatest common divisor (GCD) via Euclidean algorithm.
89+
func FindGCD(a, b int) int {
90+
for b != 0 {
91+
t := b
92+
b = a % b
93+
a = t
94+
}
95+
96+
return a
97+
}
98+
99+
// Find Least Common Multiple (LCM) via Greatest Common Divisor (GCD).
100+
func FindLCM(a, b int, values ...int) int {
101+
result := a * b / FindGCD(a, b)
102+
103+
for i := 0; i < len(values); i++ {
104+
result = FindLCM(result, values[i])
105+
}
106+
107+
return result
108+
}
109+
110+
func calcGhostSteps(route []rune, nodes map[string]Node) int {
111+
steps := make([]int, 0)
112+
113+
for from := range nodes {
114+
if !strings.HasSuffix(from, "A") {
115+
continue
116+
}
117+
118+
steps = append(steps, calcSteps(route, from, "Z", nodes))
119+
}
120+
121+
return FindLCM(steps[0], steps[1], steps...)
122+
}
123+
124+
func main() {
125+
input, err := os.Open("./2023/8/input.txt")
126+
if err != nil {
127+
log.Printf("Failed to open input.txt: %v", err)
128+
return
129+
}
130+
defer input.Close()
131+
132+
scanner := bufio.NewScanner(input)
133+
134+
route, err := parseRoute(scanner)
135+
if err != nil {
136+
log.Printf("Failed to parse routes from input: %v", err)
137+
}
138+
139+
nodes, err := parseNodes(scanner)
140+
if err != nil {
141+
log.Printf("Failed to parse nodes from input: %v", err)
142+
}
143+
144+
firstPartResult := calcSteps(route, "AAA", "ZZZ", nodes)
145+
log.Printf("Part 1 result: %d", firstPartResult)
146+
147+
secondPartResult := calcGhostSteps(route, nodes)
148+
log.Printf("Part 2 result: %d", secondPartResult)
149+
}

0 commit comments

Comments
 (0)