Advent of Code 2021 in Kotlin
These are my solutions to Advent of Code 2021 using the Kotlin language. Each day has its own subpackage.
Try the problems yourself at https://adventofcode.com/2021/.
Feel free to create a Github issue if you want to discuss anything!
Usage
- Clone this repo:
git clone https://github.com/jkpr/advent-of-code-2021-kotlin
- Open in IntelliJ IDEA
Table of contents
Day % 5 == 0 |
Day % 5 == 1 |
Day % 5 == 2 |
Day % 5 == 3 |
Day % 5 == 4 |
---|---|---|---|---|
1 · notes · code | 2 · notes · code | 3 · notes · code | 4 · notes · code | |
5 · notes · code | 6 · notes · code | 7 · notes · code | 8 · notes · code | 9 · notes · code |
10 · notes · code | 11 · notes · code | 12 · notes · code | 13 · notes · code | 14 · notes · code |
15 · notes · code | 16 · notes · code | 17 · notes · code | 18 · notes · code | 19 · notes · code |
20 · notes · code | 21 · notes · code | 22 · notes · code | 23 · notes · code | 24 · notes · code |
25 · notes · code |
Day 1
We essentially need to iterate with a window over the list. See windowed in the docs. This leads to one-line solutions for both parts.
In general, see this page on retrieving collection parts.
Kotlin's generous standard library for the win.
Day 2
Dusting off the trusty when construct. It often looks cleaner than if
/ else if
/ else
.
In the case of the code here for Day 2, an else
branch is not needed because when
is not used as an expression.
Day 3
For part 1, a scope function with is used for the first time. This is my favorite way to use a StringBuilder
:
with(StringBuilder()) {
append("Hello, ")
append("World!")
toString()
}
For part 2, an extension function is used for the first time. This simplifies things by breaking the code across different places while allowing expressive code.
Both scope functions and extension functions are some of the best features of Kotlin!
Day 4
For the first time, a class
was used to represent the board (bingo card). A board is a list of rows, and a row is a list of strings.
Two data structures are built in the Board class:
- All bingos for the board, made of the rows and columns, each as a set of Strings.
- All entries on the board as a set.
When comparing against the set of all called numbers, it is easy to check for bingo with
fun hasBingo(called: Set<String>) = bingos.any { called.containsAll(it) }
and get all uncrossed numbers with
fun getUncrossed(called: Set<String>) = entries - called
We can break up the input on new lines into chunks with:
fun getChunks(input: List<str>) = input.joinToString("\n").trim().split("\n\n")
Then from there, split each chunk on new lines to get rows again.
Day 5
Today, we continue to explore Kotlin's collection methods in the standard library with flatMap
. For each line in the input, we get a list of all grid points that the line crosses. flatMap
combines all of those lists into a single list.
A few more notes. To use regex in Kotlin, an easy way to do this is to convert a raw string to a regex object, for example:
"""\d+""".toRegex().findAll(line)
Day 6
My template for solutions return integers. So when the results were coming back negative, it was not hard to figure out we were dealing with numbers larger than Int.MAX_VALUE
. (On my machine, that happens to be 2147483647 = 2^31 - 1).
The great thing about the Kotlin compiler being able to infer types is that this change only required one function signature change.
I built a map of days left to spawn as keys with counts of lanternfish as values.
In this solution, I got to use Map.mapValues
during set up to convert integer counts to long counts and Map.mapKeys
to decrease the counters.