Skip to content

Commit f2a5ff8

Browse files
committed
Solution 2025-11 (Reactor)
1 parent c21e558 commit f2a5ff8

File tree

2 files changed

+182
-0
lines changed

2 files changed

+182
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package de.ronny_h.aoc.year2025.day11
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.extensions.memoize
5+
6+
fun main() = Reactor().run(643, 417190406827152)
7+
8+
class Reactor : AdventOfCode<Long>(2025, 11) {
9+
override fun part1(input: List<String>): Long = ServerRack(input).numberOfPathsToOut()
10+
11+
override fun part2(input: List<String>): Long = ServerRack(input).numberOfPathsToOutVisitingDacAndFft()
12+
}
13+
14+
data class Device(val id: String, val outputs: List<String>)
15+
16+
fun List<String>.parseDevices() = map {
17+
val (id, outputs) = it.split(": ")
18+
Device(id, outputs.split(" "))
19+
}
20+
21+
class ServerRack(input: List<String>) {
22+
private val startId = "you"
23+
private val mainOutId = "out"
24+
private val serverId = "svr"
25+
private val dacId = "dac"
26+
private val fftId = "fft"
27+
28+
private val devicesById = input.parseDevices().associateBy { it.id }
29+
30+
fun numberOfPathsToOut(): Long = numberOfPathsBetween(startId to mainOutId)
31+
32+
fun numberOfPathsToOutVisitingDacAndFft(): Long {
33+
val fromDacToFft = numberOfPathsBetween(dacId to fftId)
34+
35+
return if (fromDacToFft != 0L) {
36+
numberOfPathsBetween(serverId to dacId) * fromDacToFft * numberOfPathsBetween(fftId to mainOutId)
37+
} else {
38+
numberOfPathsBetween(serverId to fftId) * numberOfPathsBetween(fftId to dacId) * numberOfPathsBetween(dacId to mainOutId)
39+
}
40+
}
41+
42+
private fun numberOfPathsBetween(devices: Pair<String, String>): Long {
43+
println("searching from '${devices.first}' to '${devices.second}'")
44+
45+
lateinit var numberOfPathsBetweenRec: (Pair<String, String>) -> Long
46+
numberOfPathsBetweenRec = { p: Pair<String, String> ->
47+
when (p.first) {
48+
p.second -> 1
49+
mainOutId -> 0
50+
else -> devicesById.getValue(p.first).outputs.sumOf { numberOfPathsBetweenRec(it to p.second) }
51+
}
52+
}.memoize()
53+
54+
return numberOfPathsBetweenRec(devices).also { println("-> $it") }
55+
}
56+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package de.ronny_h.aoc.year2025.day11
2+
3+
import de.ronny_h.aoc.extensions.asList
4+
import io.kotest.core.spec.style.StringSpec
5+
import io.kotest.matchers.shouldBe
6+
7+
class ReactorTest : StringSpec({
8+
9+
"parse devices" {
10+
"""
11+
aaa: you hhh
12+
you: bbb ccc
13+
""".asList().parseDevices() shouldBe listOf(
14+
Device("aaa", listOf("you", "hhh")),
15+
Device("you", listOf("bbb", "ccc")),
16+
)
17+
}
18+
19+
"part 1: the number of paths to out" {
20+
val input = """
21+
aaa: you hhh
22+
you: bbb ccc
23+
bbb: ddd eee
24+
ccc: ddd eee fff
25+
ddd: ggg
26+
eee: out
27+
fff: out
28+
ggg: out
29+
hhh: ccc fff iii
30+
iii: out
31+
""".asList()
32+
Reactor().part1(input) shouldBe 5
33+
}
34+
35+
"part 2: exactly one straight path" {
36+
val input = """
37+
svr: aaa
38+
aaa: dac
39+
dac: ccc
40+
ccc: fft
41+
fft: out
42+
""".asList()
43+
Reactor().part2(input) shouldBe 1
44+
}
45+
46+
"part 2: exactly one path with a bypass right" {
47+
val input = """
48+
svr: aaa
49+
aaa: dac bya
50+
dac: ccc
51+
bya: ccc
52+
ccc: fft
53+
fft: out
54+
""".asList()
55+
Reactor().part2(input) shouldBe 1
56+
}
57+
58+
"part 2: exactly one path with a bypass left" {
59+
val input = """
60+
svr: aaa
61+
aaa: bya dac
62+
bya: ccc
63+
dac: ccc
64+
ccc: fft
65+
fft: out
66+
""".asList()
67+
Reactor().part2(input) shouldBe 1
68+
}
69+
70+
"part 2: 2 paths above fft and dac" {
71+
val input = """
72+
svr: aaa bbb
73+
aaa: dac
74+
bbb: dac
75+
dac: ccc
76+
ccc: fft
77+
fft: out
78+
""".asList()
79+
Reactor().part2(input) shouldBe 2
80+
}
81+
82+
"part 2: 2 paths below fft and dac" {
83+
val input = """
84+
svr: aaa
85+
aaa: dac
86+
dac: bbb
87+
bbb: fft
88+
fft: ccc ddd
89+
ccc: out
90+
ddd: out
91+
""".asList()
92+
Reactor().part2(input) shouldBe 2
93+
}
94+
95+
"part 2: 2 paths between fft and dac" {
96+
val input = """
97+
svr: aaa
98+
aaa: dac
99+
dac: bbb
100+
bbb: ccc ddd
101+
ccc: fft
102+
ddd: fft
103+
fft: out
104+
""".asList()
105+
Reactor().part2(input) shouldBe 2
106+
}
107+
108+
"part 2: the number of paths from svr to out visiting ffd and dac" {
109+
val input = """
110+
svr: aaa bbb
111+
aaa: fft
112+
fft: ccc
113+
bbb: tty
114+
tty: ccc
115+
ccc: ddd eee
116+
ddd: hub
117+
hub: fff
118+
eee: dac
119+
dac: fff
120+
fff: ggg hhh
121+
ggg: out
122+
hhh: out
123+
""".asList()
124+
Reactor().part2(input) shouldBe 2
125+
}
126+
})

0 commit comments

Comments
 (0)