Kotlin and Advent of Code 2021

Posted on

This year, I'm doing Advent of Code in the Kotlin programming language. I wanted to briefly present some of my solutions. I'm also committing the solutions on GitHub as a notebook, having tried the wonderful Kotlin Jupyter kernel.

As of Day 6, my personal stats so far are below. I couldn't strictly solve the problems on time at 10:30AM IST as I am on vacation.

      --------Part 1--------   --------Part 2--------
Day       Time   Rank  Score       Time   Rank  Score
  6   00:26:09   7818      0   01:50:03  10209      0
  5   06:29:40  24271      0   06:29:48  20773      0
  4   23:23:28  52511      0   23:23:41  48883      0
  3   00:56:29  14015      0   01:04:16   7074      0
  2   00:08:19   5877      0   00:15:40   6746      0
  1   00:06:57   3878      0   00:14:02   3648      0

DAY 2

The rough idea of day 2 is to take some navigational instructions and compute the result as the product of final horizontal and vertical positions after making the moves. The full problem statement is available at https://adventofcode.com/2021/day/2. I enjoy that I could simply read the file into a list like below in Kotlin, which also sets us up nicely to do all sorts of input parsing for more complicated use cases.

val moves = File("input.txt").readLines().map { it.split(" ") }

At first, I tried using map and when

// part 1
moves.map { 
    val X = it.last().toInt()
    when(it.first()) {
        "forward" -> horizontal += X
        "up"      -> depth -= X
        "down"    -> depth += X
    }
}


// part 2
moves.map { 
    val X = it.last().toInt()
    when(it.first()) {
        "forward" -> { 
            horizontal += X
            depth = depth + (aim * X)
        }
        "up"      -> aim -= X
        "down"    -> aim += X
    }
}

but later, I noticed that it is possible to model the instructions as movement in some (X, Y) plane. Therefore, the forward, up, down becomes irrelevant. And I could leverage reduce and fold. To achieve that, here is how the moves can be parsed:

val moves = File("2.txt")
    .readLines()
    .map { it.split(" ") }
    .map { 
        val X = it[1].toInt()
        when(it[0]) {
            "forward" -> Pair(+X, 0)
            "up" -> Pair(0, -X)
            else -> Pair(0, +X)
        }
    }

and once that's done:

// part 1
val (horizontal, depth) = moves.reduce { (h, d), (x, y) -> Pair(h + x, d + y) }

// part 2
val (h, d, a) = moves.fold(
    initial = Triple(0, 0, 0),
    operation = {
        (h, d, a), (x, y) -> Triple(h + x, d + a * x, a + y)
    }
)

Day 6

Today (6th of December, 2021) is the sixth day of the Advent of Code, and below is a solution. I must admit I can already see a few possible refactoring opportunities, but it is what it is for now.

var fishes = File("6.txt")
    .readLines()[0]
    .split(",")
    .map { it.toInt() }
    .groupingBy { it }
    .eachCount()
    .mapValues { it -> it.value.toLong() }.toMutableMap()

for ( i in 1..256 ) {
    val spawns = fishes.getOrDefault(0, 0L)
    val sevens = fishes.getOrDefault(7, 0L)
    val zeroes = fishes.getOrDefault(0, 0L)
    for ( j in 0..8 ) {
        when(j) {
            6    -> fishes[6] = sevens + zeroes
            8    -> fishes[8] = spawns
            else -> fishes[j] = fishes.getOrDefault(j + 1, 0L)
        }
    }
}

fishes.values.sum()

Day 7

2021 December 07, 12:00 noon IST. Today was a bit easier:

var positions = File("7.txt")
    .readLines()[0]
    .split(",")
    .map { it.toInt() }

val median = positions
    .sorted()
    .let { (it[it.size / 2] + it[(it.size - 1) / 2]) / 2 }

fun gauss(n: Int) = n * (n + 1) / 2

positions
    .sumOf { abs(it - median) }

(1..positions.maxOrNull()!!)
    .minOf { p -> positions.sumOf {  gauss(abs(p - it)) } }