# Advent of Code 2019 - Day 11, in Kotlin

Kotlin solutions to parts 1 and 2 of Advent of Code 2019, Day 11: 'Space Police'

Posted on

Why are the Space Police after us? This ship we’re in is legitimate salvage!

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

Problem Input

Because our `IntCodeComputerMk2` uses `Long` for everything, we’ll parse the input the same way we did on Day 9:

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

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

}
``````

We’ll also annotate our class with `@ExperimentalCoroutinesApi` because we’ll end up using a property that is still experimental and requires us to opt-in.

#### Day 11, Part 1

The puzzle text can be found here.

Let’s define some constants and a helper class to get started.

Constants in `Day11` are all `Long`, because that’s what `IntCodeComputerMk2` speaks.

``````// In Day11

companion object {
private const val black: Long = 0L
private const val white: Long = 1L
private const val left: Long = 0L
private const val right: Long = 1L
}

``````

Next we’ll define a new class called `ScreenDirection`. One of the things about Advent of Code is that puzzles generally (but not always) use “screen order”. Meaning that 0,0 is in the upper left hand corner, not the lower left hand corner. So `y` increases as we move down. In case we end up with a puzzle that doesn’t behave that way, I wanted to name our direction class something specific:

``````sealed class ScreenDirection {
abstract fun turnAndMoveLeft(from: Point2D): Pair<ScreenDirection, Point2D>
abstract fun turnAndMoveRight(from: Point2D): Pair<ScreenDirection, Point2D>

object North : ScreenDirection() {
override fun turnAndMoveLeft(from: Point2D) = Pair(West, from.left())
override fun turnAndMoveRight(from: Point2D) = Pair(East, from.right())
}
object East : ScreenDirection() {
override fun turnAndMoveLeft(from: Point2D) = Pair(North, from.down())
override fun turnAndMoveRight(from: Point2D) = Pair(South, from.up())
}
object West : ScreenDirection() {
override fun turnAndMoveLeft(from: Point2D) = Pair(South, from.up())
override fun turnAndMoveRight(from: Point2D) = Pair(North, from.down())
}
object South : ScreenDirection() {
override fun turnAndMoveLeft(from: Point2D) = Pair(East, from.right())
override fun turnAndMoveRight(from: Point2D) = Pair(West, from.left())
}
}
``````

As you can see, we only implement the concepts of turning left and right. If we need more, we’ll add them later. The interesting part to notice is that if we’re facing `West` and want to turn left, towards the `South`, we need to move `up` instead of `down` which would seem more natural (because we’re using screen coordinates). This is an artifact of how I defined `up` and `down` a few days ago so feel free to change your own implementation if this strikes you as odd or confusing.

Painting The Ship

Our little painting robot works as follows:

1. Tell it what color it’s looking at
2. It tells us what color to paint.
3. And then which direction to move (left or right).

We’re going to use coroutines again, since our `IntCodeComputerMk2` already supports them. This will allow us to run the computer in one coroutine and loop around with the input in another coroutine.

``````private fun paintShip(startingWith: Long = black) = runBlocking {
val ship = mutableMapOf(Point2D.ORIGIN to 0L)
val computer = IntCodeComputerMk2(program)
var location: Point2D = Point2D.ORIGIN
var screenDirection: ScreenDirection = ScreenDirection.North
launch {
computer.runProgram()
}

computer.input.send(startingWith)
val colorMsg = computer.output.receive()
ship[location] = colorMsg
when (val dir = computer.output.receive()) {
left -> screenDirection.turnAndMoveLeft(location)
right -> screenDirection.turnAndMoveRight(location)
else -> throw IllegalStateException("Invalid direction: \$dir")
}.apply {
screenDirection = first
location = second
}
computer.input.send(ship.getOrDefault(location, black))
}

ship
}

``````

Let’s go through that. First, we’ll take in an optional color to start off with (because I know things about Part 2). We’ll also run the entire function using `runBlocking` which is a coroutine context that will block so the callers to this function don’t have to support suspending calls.

Next, we set up our variables. Our `ship` is going to be a `Map` of `Point2D` to `Long` where the value is the color on that spot. The `computer` is an instance of our `IntCodeComputerMk2`. We define `location`, which we’ll initialize with 0,0 (the origin) and `screenDirection`, which is initialized to North.

We `launch` our computer in its own coroutine so it will run continuously in the background until it halts. Before starting our work loop, we send the computer the color we are starting with on its `input`.

Now for the loop described above. The `isClosedForReceive` property is still experimental, that’s why we have the annotation on our class. While the output channel from the computer is still open, we know there is still output being produced. We read the color from `output` and set the current location to that value. Next, we receive and interpret the `direction`. Our `turnAndMove...` functions return a `Pair<ScreenDirection, Point2D>` which represent what new direction we are facing and what new location we have.

I would love it if Kotlin supported destructuring assignments so we could do this:

``````(screenDirection, location) = when(val dir = computer.output.receive()) {
left -> screenDirection.turnAndMoveLeft(location)
right -> screenDirection.turnAndMoveRight(location)
else -> throw IllegalStateException("Invalid direction: \$dir")
}
``````

But since it doesn’t, we’ll have to do that work in the `apply`. While we’re at this point we can send the computer our new location. Even though the computer might be done working, we can still send this because the `input` channel does not close until we turn off the computer.

Finally, we return the state of the ship.

Solve Part 1

Now we can solve Part 1:

``````fun solvePart1(): Int =
paintShip().size
``````

This tells us how many spots are painted. Star earned!

#### Day 11, Part 2

The puzzle text can be found here.

Remember what I said the other day about not liking visual interpretation of the output? I haven’t changed my mind. Nevertheless, let’s get cracking.

We have almost everything we need to solve this. First, let’s put a new `Comparator` in the companion for `Point2D`, one that will let us sort the points in screen order.

``````// In Point2D's companion:

val readerOrder: Comparator<Point2D> = Comparator { o1, o2 ->
when {
o1.y != o2.y -> o1.y - o2.y
else -> o1.x - o2.x
}
``````

Next, we’ll call our `paintShip` function, this time with `white` as the starting color.

``````fun solvePart2() {
val ship = paintShip(white)
val min = ship.keys.minWith(Point2D.readerOrder)!!
val max = ship.keys.maxWith(Point2D.readerOrder)!!
(min.y..max.y).forEach { y ->
println(
(min.x..max.x).map { x ->
if (ship[Point2D(x, y)] == white) '#' else ' '
}.joinToString(separator = "")
)
}
}
``````

After getting the state of the `ship` back from our computer, we need to figure out how big of a picture to paint. We want the top left and lower right hand corners. To do that, we use `minWith` and `maxWith` over the set of points, comparing using our new `readerOrder` Comparator. Once we have those, we can set up ranges and draw our letters.

Go to the console and read what it says and that’s our answer. I’m a bit bummed mine didn’t say ROCINANTE…

Problem solved, Space Police escaped, and star earned! What a busy day!