Skip to Content

Advent of Code 2017 - Day 1, in Kotlin

Kotlin solutions to parts 1 and 2 of Advent of Code 2017, Day 1: 'Inverse Captcha'

Posted on

WOO HOO! Advent of Code 2017 is finally here and I’m so excited! I’ve been looking forward to this for months, and I’ve challenged myself to post each day’s solution to Github and blog about it. Like last year, I’m going to be solving these problems in Kotlin. I might go back and revise solutions, but I’ll be sure to annotate that fact in these posts. To quote They Might Be Giants” “This could lead to excellence, or serious injury. Only one way to know, go, go go!”

The Advent of Code has two problems each day, the second unlocking after the first is solved. The second part typically relates to the first and changes an assumption. The reward for solving a problem is a star, and your ultimate goal is to collect 50 stars. There’s a funny story that goes along with each day, but I won’t recap those here. :)

Day 1, Part 1

The captcha requires you to review a sequence of digits (your puzzle input) and find the sum of all digits that match the next digit in the list. The list is circular, so the digit after the last digit is the first digit in the list.

For example:

  • 1122 produces a sum of 3 (1 + 2) because the first digit (1) matches the second digit and the third digit (2) matches the fourth digit.
  • 1111 produces 4 because each digit (all 1) matches the next.
  • 1234 produces 0 because no digit matches the next.
  • 91212129 produces 9 because the only digit that matches the next one is the last digit, 9.

This struck me as a textbook use for zipWithNext, a new function in Kotlin 1.2. Essentially, it creates pairs of elements from a stream. This is exactly what we need!

fun solvePart1(input: String): Int =
    (input + input[0])   // Circular list, re-add [0] to the end so they pair up.
        .filter { it.first == it.second }
        .map { it.first.asDigit() }

The only tricky part here is adding input[0] to the end of the input string. I do this because the requirements treat the input as a circular list, so rather than do a clunky comparison, I’ll just add it to the end and let zipWithNext take care of it for me. Once zipWithNext pairs everything up, it’s a simple matter of throwing out the pairs the don’t match and summing one of the digits in the pair.

I also defined an extension on Char to convert it to an integer. I have a feeling this might exist, but I couldn’t find it after tens of seconds of Googling.

fun Char.asDigit(): Int = if(this.isDigit()) this-'0' else throw IllegalArgumentException()

Day 1, Part 2

Now that part 1 is solved, let’s see what part 2 has in store for us:

Now, instead of considering the next digit, it wants you to consider the digit halfway around the circular list. That is, if your list contains 10 items, only include a digit in your sum if the digit 102 = 5 steps forward matches it. Fortunately, your list has an even number of elements.

For example:

  • 1212 produces 6: the list contains 4 items, and all four digits match the digit 2 items ahead.
  • 1221 produces 0, because every comparison is between a 1 and a 2.
  • 123425 produces 4, because both 2s match each other, but no other digit has a match.
  • 123123 produces 12.
  • 12131415 produces 4.

Well, the good news is that we only have to evaluate half of the input. If a character at position A matches its pair half way around the list at position B, position B will match A as well. So let’s do less work.

fun solvePart2(input: String): Int {
    val half = input.length / 2
    return input
        .subSequence(0, half)
        .filterIndexed { i, c -> c == input[i + half] }
        .map { it.asDigit() * 2 }

Again, pretty straight forward. I feel kind of bad that I didn’t write this as a single expression (always my mental goal with Kotlin, if I can). I just felt that calculating half and passing it into another function, or constantly recalculating it, or using apply was overkill. At any rate, we get half of the input string, go through each character, comparing it to it’s pair half way around the circle. The trick here is to double our answer, because again, A == B == A.

And for all that work, we earn two stars, on our way to 50! Yay!

Further Reading

  1. Index of All Solutions - All solutions for 2017, in Kotlin.
  2. My Github repo - Solutions and tests for each day.
  3. Solution - Full code for day 1
  4. Advent of Code - Come join in and do these challenges yourself!
  5. Go! Go! Go! - Some TMBG music to code by.