Whoops it's been like forever since I wrote a blog post.  Figure I'll do Advent of Code this year and write about each problem to force myself to do some amount of blogging throughout December!

Day 1 is, as always, broken into two parts.  The premise for this year is as charming as ever as we try to help Santa's Elves rescue his sleigh keys from the bottom of the ocean after an elf mishap.

The Problem, Part 1

Starting off we're given the prompt that we need to track the depth of the ocean floor from our submarine's sonar array.  It reports them as a list of integers, with integers higher in the list being closer to the submarine.  Our goal for part one is to simply decide how many of the depths display an increase from the previous depth.  Per their example, for the list

199
200
208
210
200
207
240
269
260
263

we have 7 increases.

199 
200 (increased)
208 (increased)
210 (increased)
200 
207 (increased)
240 (increased)
269 (increased)
260 
263 (increased)

Our actual data set is, unsurprisingly, quite a bit larger.  We'll be tackling this all in Kotlin, so let's get going!

Solving Part 1

Luckily this first part is very straight forward.  Given a list of integers we need to step 1 at a time, check if the integer is larger than the last one, and keep a count of the number of times that happens.  So let's do just that!

fun day1(depths: List<Int>) : Int {
    var lastDepth: Int? = null
    var numberOfIncreases = 0
    depths.forEach { depth ->
        lastDepth = if (lastDepth == null) {
            depth
        } else {
            if (lastDepth!! < depth) {
                numberOfIncreases++
            }
            depth
        }
    }
    return numberOfIncreases
}

And done.  This is pretty straight forward, but let's go over it real quick anyway.  For this we're making use of Kotlin's closures, a feature Java struggles with1, to track both the last depth and the number of times that depth was larger than the last.  lastDepth is kept as a nullable int because we want to start it at null so we can set it to depth on the first pass.  numberOfIncreases of course can be non-null as it starts at zero and only ever increments.

After that we forEach over the input list of integers, setting lastDepth each iteration using an if-expression.  This is the "Kotlin way" of doing it, but you can just as easily write it

if (lastDepth == null) {
    lastDepth = depth
} else {
    lastDepth = depth
}

if you prefer.  You'll also notice that after the first iteration, once we've set lastDepth, we check if the current depth is greater than the last depth and increment our numberOfIncreases if it is.

And just like that we have a solution!  When running it for my large data set, I get a count of 1791, earning us our first star. Onward to part 2!

Part 2: Submarine Boogaloo

For part two we're told that the data we're getting back is fuzzy, and so our original methodology isn't accurate enough.  Instead, we need to take every set of three consecutive elements from our list and check those instead.  Each number can be in up to 3 sets (the set starting 2 numbers prior, 1 number prior, and the set starting with itself), so we need to adjust our approach just a tiny bit.  For some clarity, here is their example which illustrates the idea nicely:

199  A      
200  A B    
208  A B C  
210    B C D
200  E   C D
207  E F   D
240  E F G  
269    F G H
260      G H
263        H

One thing to note is that every set is exactly three numbers, so the last two numbers are part of, at most, two sets rather than three.

Luckily for us, while this seems like a huge change we can actually make total use of our existing code, without making any changes to it, we just need to reprocess the input.

fun day1Part2(depths: List<Int>) : Int {
    val depthSets = mutableListOf<Int>()
    var index = 0
    while (index < depths.size - 2) {
        depthSets.add(
            depths[index] + depths[index + 1] + depths[index + 2]
        )
        index++
    }
    return day1(depthSets)
}

Rather than write new code that needs to do everything day1 was doing but in a more complex manor, it's better for us to instead just take the input we get, write a simple while loop to accumulate the depths, and let our existing, tested code run the new data2.  The code requires very little explanation, and the only gotcha we need to worry about is making sure we don't exceed the size of our input data, which is easily handled in the while condition.

Using this method we find that we have 1822 increases, earning our second golden star!

And with that, we've closed out day 1 of Advent of Code 2021.  I plan to force myself to do every day and write about it too, so wish me luck!

Notes

  1. Java's lambda closure doesn't allow for us to change references.  You can reference only final values outside of a lambda (though you can change the internal state of the object).  Kotlin isn't bound by this restriction, allowing easier handling of closure variables like integers.

2. This is actually a wonderful example of code reuse, while we could certainly do this in a single loop of the data, we would have to write an entirely new function to test.  Instead, by simple modifying the input we can make use of a battle tested method and avoid re-inventing the wheel.