# Advent of Code 2022 - Day 3, in Kotlin - Rucksack Reorganization

Kotlin solutions to parts 1 and 2 of Advent of Code 2022, Day 3: 'Rucksack Reorganization'

Posted on

Today’s puzzle gives us an opportunity to use `reduce` and `chunked`, two functions I use all the time when solving Advent of Code problems. The puzzle today as a nice one for illustrating various aspects of how `Strings` can be manipulated, and I had a lot of fun with it.

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

Puzzle Input

#### ⭐ Day 3, Part 1

The puzzle text can be found here.

Let’s start and the end first by defining an extension function on `Char` called `priority`. This will implement the priority logic on a given character.

``````// In Day03

private fun Char.priority(): Int =
when (this) {
in 'a'..'z' -> (this - 'a') + 1
in 'A'..'Z' -> (this - 'A') + 27
else -> throw IllegalArgumentException("Letter not in range: \$this")
}
``````

The `priority` function makes nice use of Kotlin’s `when` expression. It also takes advantage of the fact that we can subtract characters from one another and get their ordinal values. In our case, we take the value of the character and subtract either `a` or `A` (depending on which case we’re using) and then add back an offset (either 1 or 27, again depending on the case). When called on a `Char`, this function will tell us the `priority` according to the instructions in the puzzle.

The instructions ask us to find a letter that is common to both the fist half and second half of each line of `input`. We can get the first and second half of each `String` by calling `substring` on it, but then what? Since we don’t care how many matches we have, and duplicate characters on any one side of the dividing line don’t really matter, if we could put all the `Char`s into a `Set<Char>` and then `intersect` them, we would have a single `Char` that is common to both sets.

Let’s implement this logic in a way that might seem a little odd now but will make sense when we get to part two.

``````// In Day03

private fun List<String>.sharedItem(): Char =
map { it.toSet() }
.reduce { left, right -> left intersect right}
.first()
``````

I know there is a lot going on in `sharedItem`, so let’s go over it. Our `sharedItem` function is an extension function on `List<String>`. This allows us to pass any number of strings to our function and we will get the single `Char` that is common to all of them. Note, in real life we would probably have some kind of error checking (what if there was more than one match? Or no matches?) but since this is an Advent of Code puzzle, we can take the requirements as-is and assume the input will conform to it.

The first thing we do in this function is convert each `String` to a `Set<String>` via `map`. Once we have a `List<Set<String>>`, we can call `reduce` on them. The `reduce` function lets us take our `List<Set<Char>>` and reduce it down to a single `Set<Char>` according to whatever logic we want. When we write a `reduce`, we look at each pair of items in succession and perform some action. In our case, we’re going to `intersect` each of the `Set<Char>`s together. Eventually this will result in a `Set<Char>` that has a single `Char` in it, which we will retrieve with `first()`.

To call this function, let’s write another function to split our `String` and find the common `Char`:

``````// In Day03

private fun String.sharedItem(): Char =
listOf(
substring(0..length / 2),
substring(length / 2)
).sharedItem()
``````

This `sharedItem` is an extension function on `String`. I wanted to name these the same to show you that we can do this - Kotlin is smart enough to know what type we’re calling `sharedItem` on and disambiguates this call for us. In this case, our function sets up a `List<String>` with two elements - the first half of the `String` and the second half of the `String`. We get these by calling `substring` and knowing the `length` of the `String`.

I know that seems overkill - why not just `intersect` these `String`s here after converting them to `Set<Char>`? Because when we get to Part Two, we’ll need to intersect multiple `String`s and the hard work will already be done.

All that’s left is to use our good friend `sumOf` again to go over every line of input, get the `sharedItem` and then find its `priority`. Adding these together gives us our answer!

``````// In Day03

fun solvePart1(): Int =
input.sumOf { it.sharedItem().priority() }
``````

Star earned! Onward!

#### ⭐ Day 3, Part 2

The puzzle text can be found here.

See? I told you the hard work was done. In fact, the solution to Part Two is not all that different than the solution to Part One. The only real difference is our call to `chunked`. The `chunked` function is one of my favorites in the Kotlin Standard Library because I find uses for it all the the time (especially in Advent of Code). When we call `chunked(3)` on our `List<String>`, we will get back a `List<List<String>>` where each inner `List<String>` is 3 elements long. For example, if we have `["A", "B", "C", "D", "E", "F"]` as a `List<String>` and call `chunked(3)` on it, we get `[["A", "B", "C"], ["D", "E", "F"]]`. Two inner lists, each containing 3 strings. This is exactly what we need to break our `input` into chunks of three strings.

Because our `List<String>.sharedItem()` function takes any number of `String`s, we don’t have to write any new logic to handle three vs. two `String`s. We did all the hard work in Part One!

Now that we know all about `chunked`, we can write our solution.

``````// In Day03

fun solvePart2(): Int =
input.chunked(3).sumOf { it.sharedItem().priority() }
``````

Star earned! See you tomorrow.