> > Advent of Code 2023: Day 1 Part 1 – Calibrating Snow Operations

# Advent of Code 2023: Day 1 Part 1 – Calibrating Snow Operations

I’ve never yet taken part in the Advent of Code, and judging by how much time it took me to solve both parts of Day 1, it’s debatable as to whether I will have completed all challenges by Christmas Eve.

But it’s an excuse to test out my rather rudimentary Kotlin skills, and share what I learn along the way.

Caution: This post contains spoilers.

## Puzzle Description

The puzzle opens with a Christmas-y narrative about the malfunctioning global snow production and the Elves’ unconventional solution – a trebuchet. However, things take an unexpected turn when it’s revealed that a young Elf, in an attempt to showcase artistic skills, has amended the calibration document.

Our mission?

Recover the calibration values from each line and determine the sum of all these values.

## Understanding the Problem

The newly-improved calibration document is a collection of lines, each containing a calibration value.

To extract this value, we need to combine the first and last digits of each line, forming a two-digit number.

The provided example illustrates this process:

``````1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet``````

The calibration values for these lines are `12`, `38`, `15`, and `77` respectively, resulting in a sum of `142`.

Our goal is to calculate the sum of all calibration values for the entire document.

You can find the instructions in full by clicking here.

## Writing Test Cases

There are no formal unit tests provided for Advent of Code challenges, as you can complete the tasks in any programming language… or I guess, even manually, if you are so inclined.

As such I had to set up my own Kotlin project, and then create my own unit tests.

Setting up a new Kotlin project is pretty easy – just follow the instructions provided by JetBrains.

After that, I created the following unit tests:

``````// src/test/kotlin/com/codereviewvideos/aoc23/day1a/CalibratorTest.kt

package com.codereviewvideos.aoc23.day1a

import org.junit.jupiter.api.Test
import java.io.File
import kotlin.test.assertEquals

class CalibratorTest {
@Test
fun `1abc2 returns 12`() {
assertEquals(12, Calibrator.calibrate("1abc2"))
}

@Test
fun `pqr3stu8vwx returns 38`() {
assertEquals(38, Calibrator.calibrate("pqr3stu8vwx"))
}

@Test
fun `a1b2c3d4e5f returns 15`() {
assertEquals(15, Calibrator.calibrate("a1b2c3d4e5f"))
}

@Test
fun `treb7uchet returns 77`() {
assertEquals(77, Calibrator.calibrate("treb7uchet"))
}
}```Code language: Kotlin (kotlin)```

With those tests in place, I went to work.

## The Initial Implementation

I have to admit, I didn’t have an initial idea of an immediate solution in mind.

My first thought went to some kind of iteration. Maybe regex, if I hit a wall.

Typically with these code puzzles I find whatever monstrosity I come up with, some uber boffin will have found a saucy little one liner that neatly solves the puzzle, and simultaneously makes me feel like an abject failure.

Anyway, by sheer good fortune, it turns out that Kotlin provides some methods that work on strings that very conveniently give us an immediate solution:

``````package com.codereviewvideos.aoc23.day1a

class Calibrator {
companion object {
fun calibrate(s: String): Int {
val first = s.first { it.isDigit() }
val last = s.last { it.isDigit() }
return (first.toString() + last).toInt()
}
}
}```Code language: Kotlin (kotlin)```

I actually didn’t come up with the idea of using a `companion object`.

This was what IntelliJ provided when I alt + clicked on the test method – `Calibrator.calibrate("1abc2")` – which, at the time, didn’t exist.

The `companion object` was the auto-generated code it created.

In hindsight that is probably because I didn’t tell the tests to create a `new` instance of my class at any point. Knowing a bit more about Java now than I did when I first tried Kotlin, I now know this is akin to a `static class`.

OK, onwards.

Originally I’d gone with `first` – just looking at the very first character in the string, and was looking for a way to find if it was a number.

Very fortunately, Kotlin provides two variations of `first` and its friend, `last`.

If you take the simple method called of `yourString.first()` or `"a string".last()`, you get the first / last character. There’s a conversion here. From a string, to a character. That’s kinda unsettling coming from a language like JavaScript, but it’s happening and you need to be aware of it.

However, there’s a second form of `first` / `last`. A more interesting form.

``<code>val first = s.first { it.isDigit() }</code>`Code language: Kotlin (kotlin)`

This expression finds the first character in the string `s` that is a digit. The `it.isDigit()` is a predicate that checks if a character is a digit.

You could actually re-write this to be:

``val first = s.first({ it -> it.isDigit() })`Code language: Kotlin (kotlin)`

Which is valid code, and perhaps easier to understand. But it isn’t idiomatic Kotlin. If at all interested in this syntax, I covered it in more detail in this post, under the Trailing Lambda section.

The really nice thing about this function is it finds the first digit without us needing to write any further code. It checks character by character, starting from the `first` (or `last`) character, and works its way through the whole string until it finds a match.

This really neatly solved the problem.

The only quirk I had to address is how to return both digits, concatenated, without adding them:

``(first.toString() + last).toInt()`Code language: Kotlin (kotlin)`

Quirky, but after a bunch of attempts using template strings, this was the final suggested representation by IntelliJ.

OK, so problem solved. At least for a single line.

## Handling Multiple Lines

The next part of this challenge is that we need to handle multiple lines of input.

The initial documentation gives us four example lines.

I decided to craft my own test case.

``````
@Test
fun `calibrate multiple lines returns a sum of all values`() {
val input =
"""
1ten0
te10n
"""
.trimIndent()
assertEquals(20, Calibrator.calibrate(input))
}```Code language: Kotlin (kotlin)```

The challenge here is that we need to process more than one line. We already have the logic for one line.

How to solve for multiple?

``````package com.codereviewvideos.aoc23.day1a

class Calibrator {
companion object {
fun calibrate(s: String): Int {
val split = s.split("\n")
return split.map { calibrateLine(it) }.reduce { acc, value -> acc + value }
}

private fun calibrateLine(s: String): Int {
val first = s.first { it.isDigit() }
val last = s.last { it.isDigit() }
return (first.toString() + last).toInt()
}
}
}
```Code language: Kotlin (kotlin)```

There may be a better way to do this. I wouldn’t be surprised.

What I ended up doing was keeping the `calibrate` method, but moving the logic I had for one line to be a `private` function. You can’t directly call `calibrateLine`, but you can call `calibrate` with one or more lines. An easier API for the caller, whilst hiding away the complexity.

After this I did the usual thing I do with working on multiple lines of input.

Split the string, run a map over the resulting array of individual strings, and then in this case, reduce the resulting list of `Int` values down to a single number.

That works fine, but I figured there must be a `sum` method on a list of numbers. And there is:

``return split.map { calibrateLine(it) }.sum()`Code language: Kotlin (kotlin)`

Which also works, and is much neater.

IntelliJ then prompted me to go one step further:

Such that the final code I came up with was:

``````package com.codereviewvideos.aoc23.day1a

class Calibrator {
companion object {
fun calibrate(s: String): Int {
val split = s.split("\n")
return split.sumOf { calibrateLine(it) }
}

private fun calibrateLine(s: String): Int {
val first = s.first { it.isDigit() }
val last = s.last { it.isDigit() }
return (first.toString() + last).toInt()
}
}
}```Code language: Kotlin (kotlin)```

With a working implementation, I could now get the answer to the first part of the Advent of Code Day 1 puzzle.

In order to get the answer, I needed a way to run the full input through my implementation.

I wondered if I ought to create some kind of HTTP `GET` against the text document. But then I figured why not just copy / paste the data to my project?

What I ended up doing was pasting the data in to a file at `src/main/resources/input.txt`

Then I needed to figure out how to load a file from the `resources` directory in Kotlin. That took a bit of Stack Overflowing:

``````  @Test
fun `calibrate entire input file contents returns the puzzle answer`() {
// Get the file path within the resources directory

// Check if the file path is not null
if (filePath != null) {
val file = File(filePath)

// Read the contents of the file

// Print or use the file content as needed
assertEquals(53386, Calibrator.calibrate(content))
}
}
```Code language: Kotlin (kotlin)```

The most curious line is surely the one highlighted.

For your and my benefit, here’s what ChatGPT had to say about what the heck is happening here:

1. `object {}`: This is an anonymous object declaration in Kotlin. The empty curly braces `{}` define the body of the object. In this case, it’s an empty object used for accessing its class.
2. `.javaClass`: The `javaClass` property returns the runtime class of the object. It is equivalent to calling `this.getClass()` in Java.
3. `.classLoader`: The `classLoader` property retrieves the class loader for the class. The class loader is responsible for loading classes during runtime.
4. `.getResource("input.txt")`: This method is used to retrieve a URL representing the specified resource. In this case, it’s looking for a resource named “input.txt” in the classpath.
5. `?.file`: The safe call operator `?.` is used to safely invoke the `file` property on the URL obtained from `getResource("input.txt")`. If the URL is null (e.g., if the resource is not found), the entire expression evaluates to null. If the URL is not null, it retrieves the file component of the URL.
6. `val filePath = ...`: The result of the entire expression is assigned to the variable `filePath`. This variable will either hold the file path as a string or be null if the resource is not found.

Frankly I think this is way more complex than it ought to be for what must surely be a fairly common task.

I didn’t actually know the answer would be `53386`.

I ran the test with a random number in there, then updated the test when it failed, kindly spitting out that number as the “failing” value.

Anyway that gained me my first ever Gold Star on an Advent of Code challenge. Hoorah.

Pasting the answer in immediately gave me access to Part Two of the puzzle… which was quite a bit harder, and proved my initial implementation wasn’t sufficient to cover the next set of requirements.

But that’s for another post.

This site uses Akismet to reduce spam. Learn how your comment data is processed.