-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay20.kt
125 lines (95 loc) · 4.03 KB
/
Day20.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package adventofcode2023
import java.io.File
import java.util.LinkedList
object Day20 {
private const val BROADCASTER = "broadcaster"
private const val RX_OUTPUT = "rx"
private val inputs = File("resources/adventofcode2023/Day20.txt")
.readLines()
.map { line ->
val split = line.split(" -> ")
Pair(split[0], split[1].split(", "))
}
private val modules: Map<String, Module> = inputs.associate { line ->
val name = line.first.removePrefix("%").removePrefix("&")
val module =
when (line.first.first()) {
'%' -> FlipFlopModule(line.second)
'&' -> ConjunctionModule(line.second, inputs.filter { it.second.contains(name) }.map {
it.first.removePrefix("%").removePrefix("&")
})
else -> BroadcastModule(line.second)
}
name to module
}
private sealed class Module(val outputs: List<String>) {
abstract fun send(pulse: Boolean, input: String): Map<String, Boolean>
open fun reset() {}
}
private class BroadcastModule(outputs: List<String>) : Module(outputs) {
override fun send(pulse: Boolean, input: String): Map<String, Boolean> =
outputs.associateWith { pulse }
}
private class FlipFlopModule(outputs: List<String>) : Module(outputs) {
var enabled = false
override fun send(pulse: Boolean, input: String): Map<String, Boolean> =
if (pulse) emptyMap()
else {
enabled = !enabled
outputs.associateWith { enabled }
}
override fun reset() { enabled = false }
}
private class ConjunctionModule(outputs: List<String>, val inputs: List<String>) : Module(outputs) {
var lastPulses = inputs.associateWith { false }.toMutableMap()
override fun send(pulse: Boolean, input: String): Map<String, Boolean> {
lastPulses[input] = pulse
return outputs.associateWith { !lastPulses.values.all { it } }
}
override fun reset() { lastPulses = inputs.associateWith { false }.toMutableMap() }
}
private fun pushButton(onEveryPulse: (String, Boolean, String) -> Unit) {
val queue = LinkedList(listOf(Triple(BROADCASTER, false, "")))
while (queue.isNotEmpty()) {
val (destination, pulse, from) = queue.removeFirst()
val module = modules[destination]
onEveryPulse(destination, pulse, from)
module?.send(pulse, from)?.forEach {
queue.add(Triple(it.key, it.value, destination))
}
}
}
private fun highLowPulsesCounts(times: Int): Map<Boolean, Long> {
val pulseCount = mutableMapOf(true to 0L, false to 0L)
for (i in 1..times) {
pushButton { _, pulse, _ ->
pulseCount[pulse] = pulseCount[pulse]!!.plus(1)
}
}
return pulseCount
}
private fun pushesToGetRx(): Long {
val beforeRxName = modules.filter { it.value.outputs.contains(RX_OUTPUT) }.map { it.key }.single()
val beforeRxInputsCounts = modules.filter { it.value.outputs.contains(beforeRxName) }.mapValues { 0L }.toMutableMap()
var pushCount = 0L
while (beforeRxInputsCounts.any { it.value == 0L }) {
pushCount++
pushButton { destination, pulse, from ->
if (destination == beforeRxName && pulse) {
beforeRxInputsCounts[from] = pushCount
}
}
}
return beforeRxInputsCounts.values.reduce { acc, i -> lcm(acc, i) }
}
private fun gcd(a: Long, b: Long): Long = if (b == 0L) a else gcd(b, a % b)
private fun lcm(a: Long, b: Long): Long = a * b / gcd(a, b)
fun resetModules() = modules.forEach { it.value.reset() }
fun part1() = println(highLowPulsesCounts(1000).values.reduce { acc, i -> acc * i })
fun part2() = println(pushesToGetRx())
}
fun main() {
Day20.part1()
Day20.resetModules()
Day20.part2()
}