# Advent of Code 2022 - Day 20, in Kotlin - Grove Positioning System

Kotlin solutions to parts 1 and 2 of Advent of Code 2022, Day 20: 'Grove Positioning System'

Posted on

When I first read today’s puzzle I thought it would be really easy. It turns out that the sample data omits a condition that our real data has, and that threw me off for a good amount of time. It’s important to not make assumptions about your input!

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

Puzzle Input

We won’t do any special parsing with our `input` today. Since we’ll have to do this for each part of the puzzle, we’ll declare it as a `List<String>` property in the constructor and move on.

``````class Day20(private val input: List<String>) {

}
``````

#### ⭐ Day 20, Part 1

The puzzle text can be found here.

Before we get too far into the solution, let me tell you what messed me up for a non-trivial amount of time. My list had duplicates and that means I can’t always say “give me the index of the first 5” because there might be more than one 5, and they might have been shifted around so that the first 5 is really the second 5.

So we need not only keep track of the numbers to move in order, but their precise position within the original and target lists. One way to do this is to use a data class (or a `Pair`, if you want) to store the `originalIndex` of the number from the `input` and the `value` itself. When we look up a number, we find it by its `originalIndex`. That way, we know the 5 we get is the correct 5, not some shifty imposter 5.

We’ll call our tracking-class `MappedNumber`.

``````// In Day20

private data class MappedNumber(val originalIndex: Int, val value: Long)
``````

Why is `value` a `Long` and not an`Int`? Because in Part Two we deal with large numbers. Let’s parse these with `parseInput` using `mapIndexed`, and return the result as a `MutableList<MappedNumber>`.

``````// In Day20

private fun parseInput(L): MutableList<MappedNumber> =
input.mapIndexed { index, value ->
MappedNumber(index, value.toLong())
}.toMutableList()
``````

Before we get to the decryption function, let’s write the `groveCoordinates` function we’ll need to give us the answer.

``````// In Day20

private fun List<MappedNumber>.groveCoordinates(): Long {
val zero = indexOfFirst { it.value == 0L }
return listOf(1000, 2000, 3000).sumOf { this[(zero + it) % size].value }
}
``````

As you can see, we can look up the location of zero directly - my input only had one of them. Maybe intentionally? We create a list of 1000, 2000, and 3000, and add the index of the `zero` to it, and modulus the size of the list to account for the fact that the list is circular. This gives us the `MappedNumber` objects we’re looking for, so we can take the `sumOf` their `value`.

On to decryption!

``````// In Day20

private fun MutableList<MappedNumber>.decrypt() {
indices.forEach { originalIndex ->
val index = indexOfFirst { it.originalIndex == originalIndex }
val toBeMoved = removeAt(index)
}
}
``````

Since we need to go through our `input` in order, and account for duplicates, we need to find the `MappedNumber` in our list via its `originalIndex`. The way I’ve chosen to do this is to get the `indicies` off the current list, which will be the same size (we could have also explicitly set up a range, which is what I did at first before I remembered `indices`). Once we find the `index` of the `MappedNumber` we’re after, we remove it from the list, and set it aside in `toBeMoved`. Then we `add` it back in, accounting for the fact that we need to use `mod` in order for the circular list thing to work out.

And now can put all this together in a single `solvePart1` function to get our answer:

``````// In Day20

fun solvePart1(): Long {
val theList = parseInput()
theList.decrypt()
return theList.groveCoordinates()
}
``````

Star earned! Onward!

#### ⭐ Day 20, Part 2

The puzzle text can be found here.

In order to account for the `decryptionKey` we need to do a bit of refactoring to our `parseInput` function. Since Kotlin has default values, we can set the default to 1 so we don’t have to alter `solvePart1`. In our case, that wouldn’t be a big deal. But if you maintain a larger system with more than one user of a service or something, adding a defaulted value is a nice way to not break your callers.

The only change to the implementation is to multiply the `value` by the `decriptionKey` before storing it in the `MappedNumber`.

``````// In Day20

private fun parseInput(decryptionKey: Long = 1L): MutableList<MappedNumber> =
input.mapIndexed { index, value ->
MappedNumber(index, decryptionKey * value.toLong())
}.toMutableList()
``````

We’re now able to write our `solvePart2` function. We call `parseInput` with the `decryptionKey`, and get a list similar to what we had in Part One. We’ll `repeat` the `decrypt` process 10 times, and then get the `groveCoordinates` to calculate our answer.

``````// In Day20

fun solvePart2(): Long {
val theList = parseInput(811589153)
repeat(10) {
theList.decrypt()
}
return theList.groveCoordinates()
}
``````

Star earned! See you tomorrow.