DEV Community

loading...

Advent of Code 2020 in Kotlin: Day 02

Lucas
Computer goes brrrr
Originally published at heylucas.net on ・2 min read

A new day, a new challenge. Day 02 of the Advent of Code gave us a “practical” problem: There’s a database of passwords and it has inconsistent data. We need to find the passwords that match the correct format and count them.

The input is provided in a file, where each line is formatted as <number>-<number> <letter>: <password>.

How the input should be interpreted is different in parts 1 and 2.

Part 1

In this first part, the <number> items in the line indicate the minimum and the maximum (respectively) number of times <letter> should appear in the <password> for it to be valid.

Here’s how I solved it:

import java.io.File

data class PasswordEntry(
  val min: Int,
  val max: Int,
  val letter: Char,
  val password: String) {
  companion object {
    val entryRegex = Regex("""(\d+)-(\d+) +([a-z]): ([a-z]+)""")

    fun of(string: String) : PasswordEntry? {
      val matchResult = entryRegex.matchEntire(string)

      if (matchResult != null) {
        val (min, max, letter, password) = matchResult.destructured
        return PasswordEntry(min.toInt(), max.toInt(), letter[0], password)
      }
      return null
    }
  }

  fun isValid(): Boolean {
    val count = password.count({ it == letter })
    return (min <= count && count <= max)
  }
}

fun main() {
  var numberOfValidPasswords: Int = 0

  File("input.txt").forEachLine {
    val entry = PasswordEntry.of(it)

    if (entry != null && entry.isValid()) {
      numberOfValidPasswords++
    }
  }

  println(numberOfValidPasswords)
}

Enter fullscreen mode Exit fullscreen mode

How it works

The solution is quite simple: We go over each line, use a Regex to extract the components we are interested in, and then create an object representing the entry.

The way I solved it is a bit over-engineered for such a simple problem, but it let me play with a few Kotlin concepts: Regexes and companion objects.

Kotlin doesn’t support static methods like Java, which you would normally use to define a Factory method. Instead, it offers companion objects: Inside your class, you can define another that provides methods to construct the object you want.

So after we covert each line into a PasswordEntryobject, we call isValid() to determine if the password is valid. If it is, we bump the count of valid passwords.

Part 2

The solution is almost identical, but the condition to determine if a password is valid changes. The <number> entries indicate that <letter> should be present in one of those indexes, but not both at the same time.

Here’s the code:

data class PasswordEntry(
  val firstOffset: Int,
  val secondOffset: Int,
  val letter: Char,
  val password: String) {
  companion object {
    val entryRegex = Regex("""(\d+)-(\d+) +([a-z]): ([a-z]+)""")

    fun of(string: String) : PasswordEntry? {
      // ... 
       return PasswordEntry(firstIndex.toInt() - 1, secondIndex.toInt() - 1, letter[0], password)
      // ...
      }
    }

    fun isValid(): Boolean = 
      (password[firstOffset] == letter).xor(password[secondOffset] == letter)
}
Enter fullscreen mode Exit fullscreen mode

How it works

Not much changed here. Since the <number> entries represent the index, we subtract 1 from them. This way we get the correct offset in the string.

Then in the isValid() method, we use Boolean.xor to check that only of those offsets has the letter.

Done for the day

This was a nice little program to work on. I was exposed to Kotlin’s regex facilities and companion objects. Not sure how I feel about latter yet. I need to play with them for a while longer to see what’s the advantage over static methods.

Next up, Day 03.

Discussion (0)