Skip to content

Commit 343385b

Browse files
committed
2024: day 5. part 1 and 2. golang
1 parent e0c767b commit 343385b

File tree

2 files changed

+221
-0
lines changed

2 files changed

+221
-0
lines changed

src/2024/day05/1/main.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
//go:embed data.txt
11+
var fileContents string
12+
13+
type rule struct {
14+
before, after int
15+
}
16+
17+
func parse(input string) ([]rule, [][]int) {
18+
lines := strings.Split(strings.TrimSpace(input), "\n")
19+
20+
separatorIdx := -1
21+
for i, line := range lines {
22+
if line == "" {
23+
separatorIdx = i
24+
break
25+
}
26+
}
27+
28+
rules := make([]rule, len(lines[:separatorIdx]))
29+
for i, line := range lines[:separatorIdx] {
30+
parts := strings.Split(line, "|")
31+
before, _ := strconv.Atoi(parts[0])
32+
after, _ := strconv.Atoi(parts[1])
33+
rules[i] = rule{before, after}
34+
}
35+
36+
// parse updates
37+
var updates [][]int
38+
for _, line := range lines[separatorIdx+1:] {
39+
var update []int
40+
for _, num := range strings.Split(line, ",") {
41+
n, _ := strconv.Atoi(num)
42+
update = append(update, n)
43+
}
44+
updates = append(updates, update)
45+
}
46+
47+
return rules, updates
48+
}
49+
50+
func isValidUpdate(update []int, rules []rule) bool {
51+
// for quick lookups
52+
indexMap := make(map[int]int)
53+
for i, num := range update {
54+
indexMap[num] = i
55+
}
56+
57+
for _, rule := range rules {
58+
beforeIdx, beforeExists := indexMap[rule.before]
59+
afterIdx, afterExists := indexMap[rule.after]
60+
61+
if beforeExists && afterExists && beforeIdx > afterIdx {
62+
return false
63+
}
64+
}
65+
return true
66+
}
67+
68+
func main() {
69+
rules, updates := parse(fileContents)
70+
71+
sum := 0
72+
for _, update := range updates {
73+
if isValidUpdate(update, rules) {
74+
middleIdx := len(update) / 2
75+
sum += update[middleIdx]
76+
}
77+
}
78+
79+
fmt.Println(sum)
80+
}

src/2024/day05/2/main.go

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"fmt"
6+
"slices"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
//go:embed data.txt
12+
var fileContents string
13+
14+
type rule struct {
15+
before, after int
16+
}
17+
18+
func parse(input string) ([]rule, [][]int) {
19+
lines := strings.Split(strings.TrimSpace(input), "\n")
20+
21+
separatorIdx := -1
22+
for i, line := range lines {
23+
if line == "" {
24+
separatorIdx = i
25+
break
26+
}
27+
}
28+
29+
rules := make([]rule, len(lines[:separatorIdx]))
30+
for i, line := range lines[:separatorIdx] {
31+
parts := strings.Split(line, "|")
32+
before, _ := strconv.Atoi(parts[0])
33+
after, _ := strconv.Atoi(parts[1])
34+
rules[i] = rule{before, after}
35+
}
36+
37+
// parse updates
38+
var updates [][]int
39+
for _, line := range lines[separatorIdx+1:] {
40+
var update []int
41+
for _, num := range strings.Split(line, ",") {
42+
n, _ := strconv.Atoi(num)
43+
update = append(update, n)
44+
}
45+
updates = append(updates, update)
46+
}
47+
48+
return rules, updates
49+
}
50+
51+
func isValidUpdate(update []int, rules []rule) bool {
52+
// for quick lookups
53+
indexMap := make(map[int]int)
54+
for i, num := range update {
55+
indexMap[num] = i
56+
}
57+
58+
for _, rule := range rules {
59+
beforeIdx, beforeExists := indexMap[rule.before]
60+
afterIdx, afterExists := indexMap[rule.after]
61+
62+
if beforeExists && afterExists && beforeIdx > afterIdx {
63+
return false
64+
}
65+
}
66+
return true
67+
}
68+
69+
func buildGraph(update []int, rules []rule) map[int][]int {
70+
graph := make(map[int][]int)
71+
inDegree := make(map[int]int)
72+
73+
for _, num := range update {
74+
if _, exists := graph[num]; !exists {
75+
graph[num] = []int{}
76+
}
77+
}
78+
79+
for _, rule := range rules {
80+
if slices.Contains(update, rule.before) && slices.Contains(update, rule.after) {
81+
graph[rule.before] = append(graph[rule.before], rule.after)
82+
inDegree[rule.after]++
83+
}
84+
}
85+
86+
return graph
87+
}
88+
89+
func topologicalSort(update []int, rules []rule) []int {
90+
graph := buildGraph(update, rules)
91+
inDegree := make(map[int]int)
92+
result := make([]int, 0, len(update))
93+
94+
// initial in-degrees
95+
for _, num := range update {
96+
inDegree[num] = 0
97+
}
98+
for _, rule := range rules {
99+
if slices.Contains(update, rule.before) && slices.Contains(update, rule.after) {
100+
inDegree[rule.after]++
101+
}
102+
}
103+
104+
// find nodes with no incoming edges
105+
var queue []int
106+
for _, num := range update {
107+
if inDegree[num] == 0 {
108+
queue = append(queue, num)
109+
}
110+
}
111+
112+
for len(queue) > 0 {
113+
current := queue[0]
114+
queue = queue[1:]
115+
result = append(result, current)
116+
117+
for _, neighbor := range graph[current] {
118+
inDegree[neighbor]--
119+
if inDegree[neighbor] == 0 {
120+
queue = append(queue, neighbor)
121+
}
122+
}
123+
}
124+
125+
return result
126+
}
127+
128+
func main() {
129+
rules, updates := parse(fileContents)
130+
131+
sum := 0
132+
for _, update := range updates {
133+
if !isValidUpdate(update, rules) {
134+
sortedUpdate := topologicalSort(update, rules)
135+
middleIdx := len(sortedUpdate) / 2
136+
sum += sortedUpdate[middleIdx]
137+
}
138+
}
139+
140+
fmt.Println(sum)
141+
}

0 commit comments

Comments
 (0)