Skip to content

Commit a64cfdc

Browse files
committed
2025 day11
1 parent 70f8c59 commit a64cfdc

File tree

6 files changed

+177
-0
lines changed

6 files changed

+177
-0
lines changed

2025/day11/.bench

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"Lines":[{"Name":"Part 1","N":8934,"NsPerOp":126031,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0},{"Name":"Part 2","N":3385,"NsPerOp":345191,"AllocedBytesPerOp":0,"AllocsPerOp":0,"MBPerS":0,"Measured":1,"Ord":0}],"Measured":1}

2025/day11/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!-- You can add some comments here if you want to :D -->

2025/day11/main.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package main
2+
3+
import (
4+
_ "embed"
5+
"os"
6+
)
7+
8+
//go:embed input.txt
9+
var input string
10+
11+
//go:embed input_test.txt
12+
var inputTest string
13+
14+
//go:embed input_test2.txt
15+
var inputTest2 string
16+
17+
func main() {
18+
// Check argv if we use test input or not
19+
if len(os.Args) > 1 && os.Args[1] == "test" {
20+
answer := doPartOne(inputTest)
21+
println(answer)
22+
23+
answer = doPartTwo(inputTest2)
24+
println(answer)
25+
return
26+
}
27+
28+
answer := doPartOne(input)
29+
println(answer)
30+
31+
answer = doPartTwo(input)
32+
println(answer)
33+
}

2025/day11/main_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package main
2+
3+
import "testing"
4+
5+
func BenchmarkPartOne(b *testing.B) {
6+
for n := 0; n < b.N; n++ {
7+
doPartOne(input)
8+
}
9+
}
10+
11+
func BenchmarkPartTwo(b *testing.B) {
12+
for n := 0; n < b.N; n++ {
13+
doPartTwo(input)
14+
}
15+
}

2025/day11/part1.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package main
2+
3+
import "strings"
4+
5+
func doPartOne(input string) int {
6+
lines := strings.Split(strings.TrimSpace(input), "\n")
7+
8+
// Build the adjacency list graph
9+
devices := make(map[string][]string)
10+
for _, line := range lines {
11+
s := strings.Fields(line)
12+
device := strings.TrimSuffix(s[0], ":")
13+
devices[device] = s[1:]
14+
}
15+
16+
// Count all paths from "you" to "out"
17+
pathCount := countPaths(devices, "you", "out", make(map[string]bool))
18+
19+
return pathCount
20+
}
21+
22+
// countPaths uses DFS to count all paths from start to end
23+
func countPaths(graph map[string][]string, current, target string, visited map[string]bool) int {
24+
// If we reached the target, we found a path
25+
if current == target {
26+
return 1
27+
}
28+
29+
// Mark current node as visited
30+
visited[current] = true
31+
defer func() { visited[current] = false }() // Unmark when backtracking
32+
33+
totalPaths := 0
34+
35+
// Explore all neighbors
36+
for _, neighbor := range graph[current] {
37+
// Skip if already visited (avoid cycles)
38+
if visited[neighbor] {
39+
continue
40+
}
41+
42+
// Recursively count paths from neighbor to target
43+
totalPaths += countPaths(graph, neighbor, target, visited)
44+
}
45+
46+
return totalPaths
47+
}

2025/day11/part2.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package main
2+
3+
import "strings"
4+
5+
func doPartTwo(input string) int {
6+
lines := strings.Split(strings.TrimSpace(input), "\n")
7+
8+
// Build the adjacency list graph
9+
devices := make(map[string][]string)
10+
for _, line := range lines {
11+
s := strings.Fields(line)
12+
device := strings.TrimSuffix(s[0], ":")
13+
devices[device] = s[1:]
14+
}
15+
16+
// Count paths from "svr" to "out" that visit both "dac" and "fft"
17+
memo := make(map[string]int)
18+
pathCount := countPathsWithRequiredMemo(devices, "svr", "out", []string{"dac", "fft"}, make(map[string]bool), make(map[string]bool), memo)
19+
20+
return pathCount
21+
}
22+
23+
// countPathsWithRequiredMemo counts paths with memoization
24+
func countPathsWithRequiredMemo(graph map[string][]string, current, target string, required []string, visited map[string]bool, foundRequired map[string]bool, memo map[string]int) int {
25+
// If we reached the target
26+
if current == target {
27+
// Check if all required nodes were visited
28+
for _, req := range required {
29+
if !foundRequired[req] {
30+
return 0
31+
}
32+
}
33+
return 1
34+
}
35+
36+
// Create memo key from current node and which required nodes have been found
37+
memoKey := current
38+
for _, req := range required {
39+
if foundRequired[req] {
40+
memoKey += "+" + req
41+
}
42+
}
43+
44+
// Check memo
45+
if val, exists := memo[memoKey]; exists {
46+
return val
47+
}
48+
49+
// Mark current node as visited
50+
visited[current] = true
51+
defer func() { visited[current] = false }()
52+
53+
// Check if current node is a required node
54+
wasFound := make(map[string]bool)
55+
for _, req := range required {
56+
if current == req && !foundRequired[req] {
57+
foundRequired[req] = true
58+
wasFound[req] = true
59+
}
60+
}
61+
defer func() {
62+
for req := range wasFound {
63+
foundRequired[req] = false
64+
}
65+
}()
66+
67+
totalPaths := 0
68+
69+
// Explore all neighbors
70+
for _, neighbor := range graph[current] {
71+
if visited[neighbor] {
72+
continue
73+
}
74+
75+
totalPaths += countPathsWithRequiredMemo(graph, neighbor, target, required, visited, foundRequired, memo)
76+
}
77+
78+
memo[memoKey] = totalPaths
79+
return totalPaths
80+
}

0 commit comments

Comments
 (0)