Advent of Code 2019 - Day 13, in Kotlin

Kotlin solutions to parts 1 and 2 of Advent of Code 2019, Day 13: 'Care Package'

Posted on

Are you ready, player one? Today we’ll write a powerful new AI (ok, a while loop and an if statement) to play video games!

If you’d rather just view code, the GitHub Repository is here .

Problem Input

Since this is another `IntCode` input, we’ll parse it as we have in the past:

``````
@ExperimentalCoroutinesApi
class Day13(input: String) {

private val program: MutableMap<Long, Long> = input
.split(",")
.withIndex()
.associateTo(mutableMapOf()) { it.index.toLong() to it.value.toLong() }
}
``````

We will also annotate our class with `@ExperimentalCoroutinesApi` here, because we use an opt-in property that’s still experimental.

Finally, we’ll define some constants:

``````// In Day13

companion object {
private const val block = 2
private const val paddle = 3
private const val ball = 4
private const val freePlay = 2L
}
``````

Don’t worry about `freePlay`, we use that in Part 2.

⭐ Day 13, Part 1

The puzzle text can be found here.

Thankfully we have a fully working `IntCodeComputerMk2` so we can start right in on our solution. Let’s go over our strategy first:

1. Create our computer.
2. Listen for output and record the number of blocks we see.
3. Return the number of blocks.

Let’s get started!

``````fun solvePart1(): Int = runBlocking {
var blocks = 0
val computer = IntCodeComputerMk2(program = program, output = Channel(Channel.UNLIMITED))
computer.runProgram()

blocks++
}
}
blocks
}
``````

Because we aren’t feeding our computer any input, we can just run our program and then analyze the output. We don’t have to run our computer in a coroutine in the background. We’ll start by setting up our `blocks` counter which will keep track of how many blocks we’ve seen. Next, we create our `computer`, feeding it our `program`. One important detail here is the `output` channel. Because `IntCodeComputerMk2` defaults to a `CONFLATED` channel, and we want more than one output, we have to manually create an `UNLIMITED` channel. Once the computer is created we can have it run the program, and wait for it to finish (because it has all it needs, there are no inputs).

So long as `output` is generating, well, output, we can read it in groups of three. The only value we care about is the `item`, and only when it’s a block. If it is a block, we update the `blocks` counter. Since the `item` is the third read of three, we can discard the first two, we won’t use them.

Return `blocks` when we run out of output and there’s our answer.

Star earned!

⭐ Day 13, Part 2

The puzzle text can be found here.

Let’s talk about strategy before we write too much code. I found out through experimentation that it’s enough to just follow the `x` axis of where the ball and paddle are. Every time the ball moves, we want to tell the joystick to move the paddle to meet the ball. We don’t need to do anything fancy with directions, angles, or timing. We just need to be where the ball is and eventually (within a few milliseconds) the game will end.

So that’s the strategy we’ll go with: follow the ball and move the paddle towards the ball every time the ball moves.

We start by creating our computer and hacking it to allow free play (value `2` at memory address 0). That part turns out to be somewhat important, I totally misread that and was sending `2` to the computer’s `input` and nothing worked. Reading the instructions carefully turns out to be somewhat important! We also need to remember to change the `output` channel from `CONFLATED` to `UNLIMITED`, like in Part 1. After setting up our computer, we `launch` a coroutine to run its program.

``````fun solvePart2(): Int = runBlocking {
program[0] = freePlay // We r l33t H4x0rz now.
val computer = IntCodeComputerMk2(program = program, output = Channel(Channel.UNLIMITED))

launch {
computer.runProgram()
}

var score = 0
when {
x == -1 -> score = item
item == ball -> {
}
}
}
score
}
``````
Thanks to Evan R. in the #advent-of-code room in the Kotlin Slack who pointed out to me that `runBlocking` is a coroutine scope and I didn’t need to start the read loop in an `async` block!

We’ll read from the computer’s output channel until it closes. Since we don’t really care about the `y` value of anything we can just read it and discard it. So in effect we end up reading `x`, discarding the next read, and reading `item` which is either a place on the board or the score, depending on the context. Whenever `x` is -1, we’re updating the score. When the `item` is the paddle, we store the `x` location of the paddle off. Whenever the `item` is the ball, we figure out which direction the paddle needs to move (using `sign`, which comes in handy two days in a row now!), and send an `input` to the computer to move the joystick.

Once the computer halts, and we have no more output, we can return our score.

Star earned!

We’re just now over half way through with Advent of Code 2019. Just as a reminder, I’m always up for feedback on my solutions or how I explain them. Use whatever contact method you like, they are all at the bottom of the page.