Advent of Code 2024 - Day 25, in Kotlin - Code Chronicle
Kotlin solutions to Advent of Code 2024, Day 25: 'Code Chronicle'
Well, it’s Christmas morning and Advent of Code 2024 is in the books. All in all I had a very fun year. I found the vast majority of the puzzles interesting and entertaining. I did needed help a couple of times to get past math issues, or how to use graphviz better; but as always I learned a lot and I hope you did too. This was my eighth year blogging solutions and it’s hard to believe I have to wait 340 more days for Advent of Code 2025 to start! :)
If you’d rather just view the code, my GitHub Repository is here.
⭐ Day 25, Part 1
The puzzle text can be found here.
The first order of business is to parse our input into two lists of keys
and locks
. We’ll do this with a single parse
function which returns a Pair<List<IntArray>, List<IntArray>>
where the first
part of the Pair
is our locks and the second
is our keys. We do this for a couple of reasons. First, because it allows us to scan the input in a single pass, and second because the Pair
can be destructured easily into its component parts when we use it.
class Day25(private val input: List<String>) {
private fun parse(input: List<String>): Pair<List<IntArray>, List<IntArray>> {
val locks = mutableListOf<IntArray>()
val keys = mutableListOf<IntArray>()
input.chunked(8).map { pattern ->
val pins = (0..4).map { col ->
(0..6).count { row ->
pattern[row][col] == '#'
} - 1
}.toIntArray()
if (pattern[0][0] == '#') locks.add(pins) else keys.add(pins)
}
return locks to keys
}
}
We use chunked
to break the List<String>
up into 8-row sections, representing a pattern
that is either a lock or a key. Since we know how wide and high the locks and keys are, we can use ranges of 0 through 4 for width and 0 through 6 for height. If a location in the pattern
is a #
, we count that in our column total. We have to subtract 1 here because the header row (for locks) or footer row (for keys) does not count against the total and one of them is always present. We turn this into an IntArray
(mainly to make the type signatures easier, there is no real benefit to this conversion). Once we have a pattern
reduced to an IntArray
we check to see if the first character of the pattern
is a #
, indicating a lock, and if so add the pattern
to the locks
list. Otherwise we add the pattern
to the keys list.
In order to see if a key
fits into a lock
, we’ll write an infix
extension function on IntArray
to check this. We iterate through the indices
and check to make sure the total value for each pin in the lock
and key
total to no more than 5. I made this infix because I don’t think I’ve done that in a while and wanted people to see an example of how it can make code read differently. We can call fits
using two ways - either key.fits(lock)
or key fits lock
, the difference is purely subjective.
// In Day25
private infix fun IntArray.fits(lock: IntArray): Boolean =
indices.all { this[it] + lock[it] <= 5 }
And now we can solve today’s puzzle, by iterating through all of the locks
and taking the sumOf
the total count
of each lock
with each key
that fits
.
// In Day25
fun solvePart1(): Int =
parse(input).let { (locks, keys) ->
locks.sumOf { lock ->
keys.count { key -> key fits lock }
}
}
Star earned! Onward!
⭐ Day 25, Part 2
If you’ve made it this far and have 49 stars you’ll get the 50th by clicking a link in the resulting puzzle description. I want to thank everybody who has been reading along, I enjoy solving and writing these puzzles so much, and I’m glad I can share that with you.
I also want to thank Eric Wastl for dedicating ten years of his personal time to Advent of Code. Eric - I’ve learned so much from you, and am in awe at your creation.
Further Reading
- Index of All Solutions - All posts and solutions for 2024, in Kotlin.
- My Github repo - Solutions and tests for each day.
- Solution - Full code for day 25
- Advent of Code - Come join in and do these challenges yourself!