@@ -2,13 +2,15 @@ package de.havox_design.aoc2023.day20
22
33import de.havox_design.aoc2023.day20.Module.Companion.ICON_BROADCASTER
44import 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
67import kotlin.math.abs
78import kotlin.math.max
89import kotlin.math.min
910
1011class 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