Advent of Code 2023 - Day 6, in Kotlin - Wait For It
Kotlin solutions to parts 1 and 2 of Advent of Code 2023, Day 6: 'Wait For It'
What a fun problem! I’m sure there’s some clever quadratic math one could apply to get an answer without doing any kind of looping at all, but I’m happy with what I did here.
If you’d rather just view the code, my GitHub Repository is here.
Puzzle Input
We will take our puzzle input in as a List<String>
and define it as a private property because each part of the puzzle requires separate parsing.
class Day06(private val input: List<String>) {
}
Running The Race
Instead of following the puzzle text, let’s think about what we expect to see. There are some set of races where we don’t wait long enough that we expect to not set a record. Correspondingly, at the other end of the spectrum there is another set of races where we wait too long. Rather than scanning the entire possible range of races, let’s come at this from both ends.
For example:
-------------------------time---------------------------->
sssssssssssssRRRRRRRRRRRRRRRRRRRRRRRRRRRRRssssssssssssssss
--scan up---> <---scan down---
Race Results
--------------
s = Too Slow
R = New Record
We will scan UP from 1 second (since 0 makes no sense, we can’t ever set a record that way) and find the first race we set a record for. Then we scan DOWN from the highest time until we set a record. The difference between these numbers (minus one, see below) is our answer.
// In Day06
private fun race(time: Long, distance: Long): Long {
val start = (1 .. time).first { hold ->
((time-hold)) * hold > distance
} -1
val end = (time downTo 1).first { hold ->
((time-hold)) * hold > distance
}
return end-start
}
In order to run a race
we take in the time
and distance
record. We’ll do all of this with Long
instead of Int
because the numbers get longer in part 2. As stated in the strategy above, we set up a range to look at times starting at 1 and take the first
one where there is a record. Because we want to only count non-winners, we need to subtract by 1 here. The inner calculation figures out the distance we travel given a hold
time, and measures it against the distance
record.
The second range works the same way, except we go backwards from the end of the time period to the beginning, finding the first
race where we set a record.
⭐ Day 6, Part 1
The puzzle text can be found here.
Other than parsing, the race
function does all of our work for us!
// In Day06
fun solvePart1(): Long {
val times = input.first().substringAfter(":").split(" ")
.filter { it.isNotEmpty() }.map { it.toLong() }
val distances = input.drop(1).first().substringAfter(":").split(" ")
.filter { it.isNotEmpty() }.map { it.toLong() }
return times.zip(distances)
.map { race(it.first, it.second) }
.reduce(Long::times)
}
Parsing is done like in days previous - take the substringAfter
the :
, split
it by space, remove anything that is empty, and map
to Long. The only difference between parsing times
and distances
is dropping the first row of input.
In order to run the races and calculate the product, we will zip
the times
and distances
together. For each pair of them, this will give us a Pair<Long,Long>
where the first
element is the time
and the second
element is the distance
. We could probably iterate over one of these lists and look up the other by index, but I like this better.
We run each race
and then using reduce
, we multiply the results together via the Long::times
function.
Running this gives us our answer.
Star earned! Onward!
⭐ Day 6, Part 2
The puzzle text can be found here.
Again, since race
does all the work we need, we can parse our input and run it directly.
// In Day06
fun solvePart2(): Long {
val time = input.first().substringAfter(":")
.filter { it.isDigit() }.toLong()
val distance = input.drop(1).first().substringAfter(":")
.filter { it.isDigit() }.toLong()
return race(time, distance)
}
Parsing is very similar to part 1 except we filter out anything that isn’t a digit. The race
function is run as-is and gives us our answer quickly.
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 6
- Advent of Code - Come join in and do these challenges yourself!