@@ -4,21 +4,67 @@ import de.ronny_h.aoc.AdventOfCode
44import de.ronny_h.aoc.extensions.numbers.isInt
55import de.ronny_h.aoc.extensions.numbers.toIntChecked
66import de.ronny_h.aoc.year2017.day18.Instruction.*
7+ import kotlinx.coroutines.*
8+ import kotlinx.coroutines.channels.Channel
9+ import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
10+ import kotlinx.coroutines.channels.ReceiveChannel
11+ import kotlinx.coroutines.channels.SendChannel
712
8- fun main () = Duet ().run (9423 , 0 )
13+ fun main () = Duet ().run (9423 , 7620 )
914
1015class Duet : AdventOfCode <Long >(2017 , 18 ) {
11- override fun part1 (input : List <String >): Long = SoundPlayer (input.parseInstructions()).run ()
16+ override fun part1 (input : List <String >): Long = runBlocking {
17+ return @runBlocking Program (input).run ()
18+ }
19+
20+ @OptIn(ExperimentalCoroutinesApi ::class )
21+ override fun part2 (input : List <String >): Long = runBlocking {
22+ val channel0 = Channel <Long >(UNLIMITED )
23+ val channel1 = Channel <Long >(UNLIMITED )
24+ val program0 = Program (input, 0 , channel0, channel1)
25+ val program1 = Program (input, 1 , channel1, channel0)
26+
27+ val job0 = launch { program0.run () }
28+ val job1 = launch { program1.run () }
29+
30+ launch {
31+ runDeadlockDetection(program0, program1, channel0, channel1)
32+ job0.cancelAndJoin()
33+ job1.cancelAndJoin()
34+ }
35+
36+ job0.join()
37+ println (" program 0 completed" )
38+ job1.join()
39+ println (" program 1 completed" )
1240
13- override fun part2 (input : List <String >): Long {
14- return 0
41+ return @runBlocking program1.getNumberOfSends()
42+ }
43+
44+ @OptIn(ExperimentalCoroutinesApi ::class )
45+ private suspend fun runDeadlockDetection (
46+ program0 : Program ,
47+ program1 : Program ,
48+ channel0 : Channel <Long >,
49+ channel1 : Channel <Long >,
50+ ) {
51+ while (true ) {
52+ delay(100 )
53+ if (program0.isReceiving && program1.isReceiving && channel0.isEmpty && channel1.isEmpty) {
54+ println (" deadlock detected!" )
55+ return
56+ }
57+ }
1558 }
1659}
1760
18- fun List<String>.parseInstructions (): List <Instruction > = map {
61+ fun List<String>.parseInstructions (
62+ sendChannel : SendChannel <Long >? ,
63+ receiveChannel : ReceiveChannel <Long >?
64+ ): List <Instruction > = map {
1965 val parameters = it.substring(4 )
2066 when (it.substring(0 , 3 )) {
21- " snd" -> Sound (parameters)
67+ " snd" -> sendChannel?. let { Send (parameters.toValue(), sendChannel) } ? : Sound (parameters)
2268 " set" -> {
2369 val (register, value) = parameters.split(" " )
2470 SetValue (register, value.toValue())
@@ -39,13 +85,11 @@ fun List<String>.parseInstructions(): List<Instruction> = map {
3985 Modulo (register, value.toValue())
4086 }
4187
42- " rcv" -> {
43- Recover (parameters)
44- }
88+ " rcv" -> receiveChannel?.let { Receive (parameters, receiveChannel) } ? : Recover (parameters)
4589
4690 " jgz" -> {
47- val (register, value ) = parameters.split(" " )
48- JumpIfGreaterZero (register, value .toValue())
91+ val (value, offset ) = parameters.split(" " )
92+ JumpIfGreaterZero (value.toValue(), offset .toValue())
4993 }
5094
5195 else -> error(" unknown instruction: $it " )
@@ -70,80 +114,114 @@ sealed interface Value {
70114 }
71115}
72116
73- sealed interface Instruction {
74- fun executeOn (registers : MutableMap <String , Long >): Long
117+ private const val PROGRAM_NUMBER_REGISTER = " programNumber"
118+ private const val LAST_PLAYED_SOUND_REGISTER = " lastPlayedSound"
119+ private const val NUMBER_OF_SENDS_REGISTER = " numberOfSends"
75120
76- companion object {
77- private const val LAST_PLAYED_SOUND_REGISTER = " lastPlayedSound"
78- }
121+ sealed interface Instruction {
122+ suspend fun executeOn (registers : MutableMap <String , Long >): Long
79123
80124 data class Sound (private val register : String ) : Instruction {
81- override fun executeOn (registers : MutableMap <String , Long >): Long {
125+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
82126 val frequency = registers.getValue(register)
83- println (" playing frequency $frequency " )
84127 registers[LAST_PLAYED_SOUND_REGISTER ] = frequency
85128 return frequency
86129 }
87130 }
88131
132+ data class Send (private val value : Value , private val channel : SendChannel <Long >) : Instruction {
133+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
134+ val toSend = value.toNumber(registers)
135+ channel.send(toSend)
136+ val numberOfSends = registers.getValue(NUMBER_OF_SENDS_REGISTER ) + 1
137+ registers[NUMBER_OF_SENDS_REGISTER ] = numberOfSends
138+ // println("${registers[PROGRAM_NUMBER_REGISTER]}: sent $toSend [$numberOfSends]")
139+ return toSend
140+ }
141+ }
142+
89143 data class SetValue (private val register : String , val value : Value ) : Instruction {
90- override fun executeOn (registers : MutableMap <String , Long >): Long {
144+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
91145 registers[register] = value.toNumber(registers)
92146 return registers.getValue(register)
93147 }
94148 }
95149
96150 data class Add (private val register : String , val value : Value ) : Instruction {
97- override fun executeOn (registers : MutableMap <String , Long >): Long {
151+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
98152 registers[register] = registers.getValue(register) + value.toNumber(registers)
99153 return registers.getValue(register)
100154 }
101155 }
102156
103157 data class Multiply (private val register : String , val value : Value ) : Instruction {
104- override fun executeOn (registers : MutableMap <String , Long >): Long {
158+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
105159 registers[register] = registers.getValue(register) * value.toNumber(registers)
106160 return registers.getValue(register)
107161 }
108162 }
109163
110164 data class Modulo (private val register : String , val value : Value ) : Instruction {
111- override fun executeOn (registers : MutableMap <String , Long >): Long {
165+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
112166 registers[register] = registers.getValue(register) % value.toNumber(registers)
113167 return registers.getValue(register)
114168 }
115169 }
116170
117171 data class Recover (val register : String ) : Instruction {
118- override fun executeOn (registers : MutableMap <String , Long >): Long {
172+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
119173 if (registers.getValue(register) != 0L ) {
120- val frequency = registers.getValue(LAST_PLAYED_SOUND_REGISTER )
121- println (" recover last played sound's frequency: $frequency " )
122- return frequency
174+ return registers.getValue(LAST_PLAYED_SOUND_REGISTER )
123175 }
124176 return 0
125177 }
126178 }
127179
128- data class JumpIfGreaterZero (private val register : String , val value : Value ) : Instruction {
129- override fun executeOn (registers : MutableMap <String , Long >): Long {
130- if (registers.getValue(register) > 0 ) {
131- return value.toNumber(registers)
180+ data class Receive (private val register : String , private val channel : ReceiveChannel <Long >) : Instruction {
181+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
182+ val value = channel.receive()
183+ // println("${registers[PROGRAM_NUMBER_REGISTER]}: received $value")
184+ registers[register] = value
185+ return value
186+ }
187+ }
188+
189+ data class JumpIfGreaterZero (private val value : Value , private val offset : Value ) : Instruction {
190+ override suspend fun executeOn (registers : MutableMap <String , Long >): Long {
191+ if (value.toNumber(registers) > 0 ) {
192+ return offset.toNumber(registers)
132193 }
133194 return 1
134195 }
135196 }
136197}
137198
138- class SoundPlayer (private val instructions : List <Instruction >) {
139- private val registers = mutableMapOf<String , Long >().withDefault { 0 }
199+ class Program (
200+ input : List <String >,
201+ programNumber : Long = 0 ,
202+ sendChannel : Channel <Long >? = null ,
203+ receiveChannel : Channel <Long >? = null ,
204+ ) {
205+ var isReceiving = false
206+
207+ private val instructions = input.parseInstructions(sendChannel, receiveChannel)
208+ private val registers = mutableMapOf (
209+ " p" to programNumber,
210+ PROGRAM_NUMBER_REGISTER to programNumber,
211+ ).withDefault { 0 }
140212 private var instructionPointer = 0L
141213
142- fun run (): Long {
214+ suspend fun run (): Long {
215+ println (" program ${registers[PROGRAM_NUMBER_REGISTER ]} started" )
143216 while (instructionPointer in instructions.indices) {
144217 val instruction = instructions[instructionPointer.toIntChecked()]
218+ if (instruction is Receive ) {
219+ isReceiving = true
220+ }
145221 val result = instruction.executeOn(registers)
222+ isReceiving = false
146223 if (instruction is Recover && registers.getValue(instruction.register) != 0L ) {
224+ println (" program ${registers[PROGRAM_NUMBER_REGISTER ]} : instruction \" Recover\" with register not zero -> terminating" )
147225 return result
148226 }
149227 instructionPointer + = if (instruction is JumpIfGreaterZero ) {
@@ -152,6 +230,9 @@ class SoundPlayer(private val instructions: List<Instruction>) {
152230 1L
153231 }
154232 }
233+ println (" program ${registers[PROGRAM_NUMBER_REGISTER ]} : instruction pointer ran out of bounds: $instructionPointer -> terminating" )
155234 return - 1L
156235 }
236+
237+ fun getNumberOfSends (): Long = registers.getValue(NUMBER_OF_SENDS_REGISTER )
157238}
0 commit comments