# 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'

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)
add((index + toBeMoved.value).mod(size), toBeMoved)
}
}
```

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.

#### Further Reading

- Index of All Solutions - All posts and solutions for 2022, in Kotlin.
- My Github repo - Solutions and tests for each day.
- Solution - Full code for day 20
- Advent of Code - Come join in and do these challenges yourself!