Skip to content

Commit 60237b4

Browse files
committed
Solved day20 part 2 and finished day 20
1 parent 062c1b6 commit 60237b4

File tree

1 file changed

+138
-65
lines changed
  • day20/src/main/kotlin/de/havox_design/aoc2023/day20

1 file changed

+138
-65
lines changed

day20/src/main/kotlin/de/havox_design/aoc2023/day20/Day20.kt

Lines changed: 138 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ package de.havox_design.aoc2023.day20
22

33
import de.havox_design.aoc2023.day20.Module.Companion.ICON_BROADCASTER
44
import de.havox_design.aoc2023.day20.Module.Companion.ICON_BUTTON
5-
import de.havox_design.aoc2023.day20.Module.Companion.ICON_RECEIVER
5+
import de.havox_design.aoc2023.day20.Module.Companion.ICON_CONJUCTION
6+
import de.havox_design.aoc2023.day20.Module.Companion.ICON_FLIP_FLOP
67
import kotlin.math.abs
78
import kotlin.math.max
89
import kotlin.math.min
910

1011
class Day20(private var filename: String) {
1112
private val SIGNAL_DELIMITER = " -> "
13+
private val OUTPUTS_DELIMITER = ", "
1214

1315
fun solvePart1(runs: Int = 1000): Long {
1416
val (modules, assignment, conjunctionInputs) = parseInput()
@@ -39,84 +41,163 @@ class Day20(private var filename: String) {
3941

4042
@SuppressWarnings("kotlin:S3776", "kotlin:S6611")
4143
fun solvePart2(): Long {
42-
val (modules, assignment, conjunctionInputs) = parseInput()
43-
val parent = modules
44-
.filter { it.value.connections.contains(ICON_RECEIVER) }
45-
.values
46-
.first()
47-
val grandParents = modules
48-
.filter { it.value.connections.contains(parent.name) }
49-
.keys
50-
.toMutableSet()
51-
val cycles = mutableListOf<Long>()
52-
var count = 1L
53-
val countMap = grandParents
54-
.associateWith { 0L }
55-
.toMutableMap()
56-
val cycle = mutableMapOf<String, Long>()
57-
var currentAssignment = assignment
58-
var currentConjugation: Map<String, Map<String, Boolean>> = conjunctionInputs
44+
val parentName = "&lg"
45+
val modules = HashMap<String, ArrayList<String>>()
46+
var on = HashMap<String, Boolean>()
47+
var conjunctions = HashMap<String, HashMap<String, Boolean>>()
48+
49+
for (row in getResourceAsText(filename)) {
50+
val name = row.split(SIGNAL_DELIMITER)[0]
51+
modules[name] = ArrayList(
52+
row
53+
.split(SIGNAL_DELIMITER)[1]
54+
.split(OUTPUTS_DELIMITER)
55+
.map { it.trim() }
56+
.toList()
57+
)
58+
59+
when {
60+
name.startsWith(ICON_FLIP_FLOP) -> on[name] = false
61+
}
62+
}
5963

60-
while (grandParents.isNotEmpty()) {
61-
var index = 0
62-
val nextState = currentAssignment
63-
.toMutableMap()
64-
val nextConjunctionInputs = currentConjugation
65-
.mapValues { it.value.toMutableMap() }
66-
val queue = mutableListOf(Triple(ICON_BUTTON, Pulse.LOW, ICON_BROADCASTER))
64+
for (module in modules) {
65+
for (i in 0..<module.value.size) {
66+
when {
67+
modules.contains(ICON_FLIP_FLOP + module.value[i]) -> module.value[i] =
68+
ICON_FLIP_FLOP + module.value[i]
6769

68-
while (index < queue.size) {
69-
val (from, pulse, target) = queue[index]
70-
val isHigh = pulse == Pulse.HIGH
71-
val current = modules[target]
70+
modules.contains(ICON_CONJUCTION + module.value[i]) -> module.value[i] =
71+
ICON_CONJUCTION + module.value[i]
72+
}
73+
}
74+
}
75+
76+
for (module in modules) {
77+
when {
78+
module.key.startsWith(ICON_CONJUCTION) -> conjunctions[module.key] = HashMap()
79+
}
80+
}
7281

82+
for (module in modules) {
83+
for (output in module.value) {
7384
when {
74-
target in grandParents && !isHigh -> {
75-
countMap[target] = countMap[target]!! + 1
85+
conjunctions.contains(output) -> conjunctions[output]!![module.key] = false
86+
}
87+
}
88+
}
7689

77-
when {
78-
countMap[target] == 2L -> {
79-
grandParents.remove(target)
80-
cycles.add(count - cycle[target]!!)
90+
val pulses = ArrayDeque<Pair<Boolean, String>>()
91+
val sent = HashMap<Boolean, Long>()
92+
93+
sent[false] = 0
94+
sent[true] = 0
95+
96+
fun send(from: String, to: String, value: Boolean) {
97+
if (conjunctions.contains(to)) conjunctions[to]!![from] = value
98+
pulses.add(Pair(value, to))
99+
sent[value] = sent[value]!! + 1
100+
}
101+
102+
for (i in 1..1000) {
103+
send(ICON_BUTTON, ICON_BROADCASTER, false)
104+
while (pulses.isNotEmpty()) {
105+
val pulse = pulses.removeFirst()
106+
val value = pulse.first
107+
val to = pulse.second
108+
109+
when {
110+
!modules.contains(to) -> continue
111+
}
112+
113+
when {
114+
to == ICON_BROADCASTER -> for (output in modules[to]!!) {
115+
send(to, output, value)
116+
}
117+
118+
to.startsWith(ICON_FLIP_FLOP) -> when {
119+
!value -> {
120+
on[to] = !on[to]!!
121+
for (output in modules[to]!!) {
122+
send(to, output, on[to]!!)
81123
}
82124
}
125+
}
83126

84-
cycle[target] = count
127+
to.startsWith(ICON_CONJUCTION) -> {
128+
for (output in modules[to]!!) {
129+
send(to, output, !conjunctions[to]!!.all { it.value })
130+
}
85131
}
86132
}
133+
}
134+
}
135+
136+
on = HashMap(
137+
on
138+
.map { Pair(it.key, false) }
139+
.toMap()
140+
)
141+
conjunctions = HashMap(
142+
conjunctions
143+
.map { Pair(it.key, HashMap(it.value.map { dest -> Pair(dest.key, false) }.toMap())) }
144+
.toMap()
145+
)
146+
147+
val cycles = HashMap<String, Long>()
148+
var buttonCounter = 1
149+
150+
while (true) {
151+
send(ICON_BUTTON, ICON_BROADCASTER, false)
87152

88-
when (current) {
89-
null -> {
90-
nextState[target] = isHigh
91-
index++
92-
continue
153+
while (pulses.isNotEmpty()) {
154+
val pulse = pulses.removeFirst()
155+
val to = pulse.second
156+
val value = pulse.first
157+
158+
when {
159+
conjunctions[parentName]!!.contains(to) -> {
160+
when {
161+
!cycles.contains(to) && conjunctions[parentName]!![to]!! -> {
162+
cycles[to] = buttonCounter.toLong()
163+
}
164+
}
165+
166+
when (cycles.keys) {
167+
conjunctions[parentName]!!.keys -> {
168+
return cycles.values.reduce { acc, c -> lcm(acc, c) }
169+
}
170+
}
93171
}
94172
}
95173

96-
nextConjunctionInputs[target]
97-
?.set(from, isHigh)
98-
val inputs = nextConjunctionInputs[target] ?: emptyMap()
99-
val output = ModuleType.processSignal(current!!.type, isHigh, nextState[target] ?: false, inputs)
174+
when {
175+
!modules.contains(to) -> continue
176+
}
100177

101178
when {
102-
output != Pulse.NONE -> {
103-
nextState[target] = output == Pulse.HIGH
104-
current
105-
.connections
106-
.map { Triple(target, output, it) }
107-
.toCollection(queue)
179+
to == ICON_BROADCASTER -> for (output in modules[to]!!) {
180+
send(to, output, value)
181+
}
182+
183+
to[0] == ICON_FLIP_FLOP.first() -> when {
184+
!value -> {
185+
on[to] = !on[to]!!
186+
for (output in modules[to]!!) {
187+
send(to, output, on[to]!!)
188+
}
189+
}
190+
}
191+
192+
to[0] == ICON_CONJUCTION.first() -> for (output in modules[to]!!) {
193+
send(to, output, !conjunctions[to]!!.all { it.value })
108194
}
109195
}
110196

111-
index++
112197
}
113198

114-
count++
115-
currentConjugation = nextConjunctionInputs
116-
currentAssignment = nextState
199+
buttonCounter++
117200
}
118-
119-
return (cycles.lcmForList())
120201
}
121202

122203
private fun Map<String, Module>.pressTheButton(
@@ -167,14 +248,6 @@ class Day20(private var filename: String) {
167248
return nextState to nextConjunctionInputs
168249
}
169250

170-
private fun List<Long>.lcmForList(): Long =
171-
fold(1L) { lcmOfAllNumbers, number ->
172-
return when (val result = lcm(lcmOfAllNumbers, number)) {
173-
0L -> 0
174-
else -> result
175-
}
176-
}
177-
178251
private fun lcm(number1: Long, number2: Long): Long =
179252
when {
180253
number1 == 0L || number2 == 0L -> 0

0 commit comments

Comments
 (0)