Featured image of post Advent of Code 2020 in Kotlin - Introduction

Advent of Code 2020 in Kotlin - Introduction

Let's make the preparation to the 2021 Advent of Code by solving the last year puzzles and creating proper event in calendar for this year not to forget about this event

Introduction

Last year I realized about the existence of great initiative that is the Advent of Code - but it was too late to do it on time. So I left the event only looking for the defined tasks (also hearing about them at great YouTube channel Tsoding Daily - look and subscribe, this guy does the job).

This year has to be different and I will do my best not to forget about it 😋.

But to be well-prepared for this event it’s always a good strategy to look into the previous editions and try to analyze some old tasks. I’d like to solve all of them as a part of this series of posts and try to do it in the most Kotlin idiomatic approach but taking also performance into account.

The solutions will be published as a git repository after solving full series in order to simplify the process of running them but for now focus on the ideas that can be presented with these small pieces of code.

Solution template

One of the main rules in programming is DRY - don’t repeat yourself. We try to rewrite our code to remove the repeated parts and abstract its parts that can be reused. The main repeatable part of our solutions will be definitely the AdventDay which will contains our solutions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
sealed class AdventDay(private val readFromStdIn: Boolean = false) {

    abstract fun solve()

    inline fun <reified T> reads() = getInputLines()?.map { it.value<T>() }

    fun getInputLines() =
        if (readFromStdIn) generateSequence { readLine() }.toList()
        else this::class.java.getResource("/input/${this::class.java.simpleName}.in")
            ?.openStream()?.bufferedReader()?.readLines()
}

inline fun <reified T> String.value(): T = when (T::class) {
  String::class -> this as T
  Long::class -> toLongOrNull() as T
  Int::class -> toIntOrNull() as T
  else -> TODO("Add support to read ${T::class.java.simpleName}")
}

We use sealed class which can have subclasses definitions in the same compilation module and same package as the sealed class. Using a few lines of code we can define the main entrypoint of our solutions which will be capable of running all AdventDays without explicitly specifying them - it’ll be enough to inherit from our AdventDay class and implement solve to see the solution on console output.

1
2
3
4
5
6
7
fun main() = AdventDay::class.sealedSubclasses
    .mapNotNull { it.objectInstance }
    .sortedBy { it::class.java.simpleName.removePrefix("Day").toInt() }
    .forEach {
        println("--- ${it::class.java.simpleName}")
        it.solve()
    }

Using this template we can create objects as the subclasses of the AdventDay in order to have the instance of them always available and simply run our solution.

comments powered by Disqus