Advent of Code 2023 - Day 15, in Kotlin - Lens Library
Kotlin solutions to parts 1 and 2 of Advent of Code 2023, Day 15: 'Lens Library'
If you’d rather just view the code, my GitHub Repository is here.
Puzzle Input
Unlike most days this year, today we will take our input
in as a single String
and split
it into instructions
for later use.
class Day15(input: String) {
private val instructions = input.split(',')
}
⭐ Day 15, Part 1
The puzzle text can be found here.
The only real code we need to write for part 1 is something to hash
each instruction, which we will implement as an extension function.
// In Day15
private fun String.hash(): Int =
this.fold(0) { carry, char ->
((carry + char.code) * 17).rem(256)
}
Because we need to carry
a value along after calculating each character value, we can use a fold
, giving it the starting value of 0
. Each iteration through the fold will give us a new char
to evaluate and the carried value. We get the ASCII value of each char
via the code
property, multiply by 17, and take the remainder via the rem
function.
// In Day15
fun solvePart1(): Int =
instructions.sumOf { it.hash() }
Summing the hash
of each instruction
via sumOf
gives us the answer to part 1.
Star earned! Onward!
⭐ Day 15, Part 2
The puzzle text can be found here.
Part 2 will require some thought in terms of our data structure. Originally, I went with a List<List<Lens>>
where Lens
was a data class I’d created. However, I changed my mind and rewrote it as we have it below because I didn’t like having to search for the label on the lens within a list.
Instead, we will rely on a property of Kotlin’s default MutableMap
implementation which preserves insertion order. The documentation for mutableMapOf()
states this explicitly - “The returned map preserves the entry iteration order."
This will help us keep the lenses in order and if we replace of the values associated with a label the order will remain the same. This greatly simplifies the code vs. what I had before. If the documentation hadn’t explicitly called out that the insertion order is maintained, I would have used linkedMapOf
instead (which is what mutableMapOf
effectively does).
// In Day15
fun solvePart2(): Int {
val boxes = List<MutableMap<String, Int>>(256) { mutableMapOf() }
instructions.forEach { instruction ->
if (instruction.endsWith("-")) {
val label = instruction.substringBefore('-')
boxes[label.hash()].remove(label)
} else {
val label = instruction.substringBefore('=')
boxes[label.hash()][label] = instruction.substringAfter("=").toInt()
}
}
return boxes.withIndex().sumOf { (boxNumber, lenses) ->
lenses.values.withIndex().sumOf { (lensNumber, lens) ->
(boxNumber + 1) * (lensNumber + 1) * lens
}
}
}
We start out by defining 256 boxes as a List
which contains a MutableMap<String,Int>
(where the String
key is the lens label and the Int
value is the lens focal length). We initialize each member of the List
via the lambda function, specifying that we want an empty mutable map.
Next, we loop through each of the instructions
and figure out which kind we have. If we have a -
instruction, we remove
the label
from the correct box, which we calculate by calling the hash
function we wrote for part 1.
If we have an =
instruction, either replace or set the label
into the appropriate box. (Thanks to Charles Flynn on the Kotlin Slack for pointing out that I had this way more complicated than it needed to be!).
All that’s left is to sum all of the lens values. We do this via a set of nested withIndex
functions. The withIndex
function is handy for when we have a sequence of items we want along with their index. Since Kotlin doesn’t have a sumWithIndex
function, this is the next best thing. Especially with Kotlin’s support for destructuring, we can pick out the boxNumber
, lenses
, lensNumber
, and lens
from the IndexedValue
objects that the withIndex
function returns.
Star earned! See you tomorrow!
Further Reading
- Index of All Solutions - All posts and solutions for 2023, in Kotlin.
- My Github repo - Solutions and tests for each day.
- Solution - Full code for day 15
- Advent of Code - Come join in and do these challenges yourself!