# Advent of Code 2023 - Day 4, in Kotlin - Scratchcards

Kotlin solutions to parts 1 and 2 of Advent of Code 2023, Day 4: 'Scratchcards'

Posted on

Today’s puzzle is a good lesson in not getting too attached to the way data is show in the description. There is a strong temptation to store everything. And in the real world, that may actually be the right answer. But for a puzzle whose solution we really only need once, we can avoid a lot of complexity by discarding almost everything once we’ve calculated what we need.

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

Puzzle Input

Because each scratchcard is on its own row, we can take our `input` as a `List<String>`, just like in past days. We won’t declare `input` as a property because we’ll use it in the initializer to get what we need.

``````class Day04(input: List<String>) {

private val cardMatches = input.map { parseCard(it) }

private fun parseCard(input: String): Int {
val winningNumbers = input.substringAfter(":")
.substringBefore("|")
.split(" ")
.filter { it.isNotEmpty() }
.toSet()

val ourNumbers = input.substringAfter("|")
.split(" ")
.filter { it.isNotEmpty() }
.toSet()

return winningNumbers.intersect(ourNumbers).size
}
}
``````

The fun part about this puzzle is that we don’t care about most of the data once we’ve got our score figured out. We don’t care what numbers are on the card or what numbers were picked once we know how many we match. And we certainly don’t care about the card’s id so long as we can keep them in order. Given that we will only store the score for each card.

We will use `substringAfter` and `substringBefore` to pick through the `input`, and grab what we need. We don’t bother converting anything to integers because Strings will compare the same way, so why waste the effort?

We make two sets - one for the `winningNumbrs` and one for `ourNumbers`. The only part that we store, the score, is calculated by measuring how many elements these two sets have in common via the `intersect` function.

#### ⭐ Day 4, Part 1

The puzzle text can be found here.

The only thing left to do for part 1 is to count up the scores, using the formula provided. Because Kotlin doesn’t have a built in function to raise an `Int` to an `Int` power, we will have to call the existing `pow` function off of a `Double` (2.0).

``````// In Day04

fun solvePart1(): Int =
cardMatches.sumOf { 2.0.pow(it-1).toInt() }
``````

Once again, we use the ever-helpful `sumOf` function to sum up the scores of all our `cardMatches`. We do this by raising 2 to the score-1’s power and casting the result back to an `Int`.

Star earned! Onward!

#### ⭐ Day 4, Part 2

The puzzle text can be found here.

For part 2 our data is in a usable format so our calculation is rather straight forward.

``````// In Day04

fun solvePart2(): Int {
val cards = IntArray(cardMatches.size) { 1 }
cardMatches.forEachIndexed { index, score ->
repeat(score) {
cards[index+it+1] += cards[index]
}
}
return cards.sum()
}
``````

We will declare an `IntArray` to represent how many `cards` of each id we have. We seed this array with `1` in each spot because we have one of each card.

Next, we look through our `cardMatches` list which contains the scores for each card. Using `forEachIndexed` lets us know the place within the `cardMatches` array as well as the value of the array (the `score`). We use these facts to calculate how many cards we just won.

Finally, we `repeat` a calculation `score` number of times, which allows us to calculate how many cards we need to add to the next `score` number piles of cards. I think there’s probably a way I could have used some version of `fold` or `reduce` here, but this seems clearer to me. In the real world, we would do some bounds checking here to make sure we don’t attempt to write to an element in `cards` that doesn’t exist, but the puzzle explicitly states this cannot happen, so I’m not going to write code to handle that.

Star earned! See you tomorrow!