Advent of Code 2020 in Kotlin: Day 04
Tags: kotlin, adventofcode
Published: 2020-12-06 05:15:20

---
title: Advent of Code 2020 in Kotlin: Day 04
published: true
date: 2020-12-04 15:39:00 UTC
tags: Kotlin,adventofcode
canonical_url: https://heylucas.net/2020/12/04/advent-of-code-2020-in-kotlin-day-04/
---

[Day 04](https://adventofcode.com/2020/day/4) was Advent of Code’s version of _[Papers, Please](https://en.wikipedia.org/wiki/Papers,_Please)_. You had to go over a list of passports and validate them. I’m not too happy with how the code turned out, as it’s a bit verbose for my taste. Alas, it’s not terrible.

## Data representation

A passport must contain a set of fields and we need to ensure they are present. I created a `Passport` class with an entry for each of those fields:

```kotlin
data class Passport(
    val byr: Int?,
    val iyr: Int?,
    val eyr: Int?,
    val hgt: String?,
    val hcl: String?,
    val ecl: String?,
    val pid: String?,
    val cid: String?) { }
```

You’ll see I mark each field as _nullable_ (the ? after the type). I do this is because each passport maybe have missing fields, making them invalid.

To help me construct the Passport objects, I defined a Builder class:

```kotlin
data class Passport(
  ...
) {
    class Builder {
      var byr: Int? = null
      var iyr: Int? = null
      var eyr: Int? = null
      var hgt: String? = null
      var hcl: String? = null
      var ecl: String? = null
      var pid: String? = null
      var cid: String? = null

      fun setField(fieldName: String, fieldValue: String) {
        when (fieldName) {
          "byr" -> byr = fieldValue.toInt()
          "iyr" -> iyr = fieldValue.toInt()
          "eyr" -> eyr = fieldValue.toInt()
          "hgt" -> hgt = fieldValue
          "hcl" -> hcl = fieldValue
          "ecl" -> ecl = fieldValue
          "pid" -> pid = fieldValue
	  "cid" -> cid = fieldValue
      }
    }

    fun build() = Passport(
      byr, iyr, eyr,
      hgt, hcl, ecl,
      pid, cid)
    }
}
```

## Reading the data

Like on [Day 03](https://dev.to/heylucas/advent-of-code-2020-in-kotlin-day-03-2bf1), I defined an extension method on the `File` class. This method returns a _nullable_ list of `Passport`s.

```kotlin
fun File.toPassportListOrNull() : List<Passport>? {
  val result = ArrayList<Passport>()
  var passportBuilder = Passport.Builder()
    
  forEachLine {
    if (it.isEmpty()) {
      result.add(passportBuilder.build())
      passportBuilder = Passport.Builder()
    } else {
      it.split(" ").forEach {
        val parts = it.split(":")
        passportBuilder.setField(parts[0], parts[1])
      }
    }
  }

  result.add(passportBuilder.build())

  return result
}
```

Parsing the passports is a bit tricky. The fields might span multiple lines. An empty line marks the end of a passport record. That’s why in the code above I check if the current line is empty to insert the passport in the list.

## Counting the valid passports

To check if a passport is valid or not, I just added a new method to the Passport class:

```kotlin
    fun isValid(): Boolean {
	return (
	    byr != null &&
	    iyr != null &&
	    eyr != null &&
	    hgt != null &&
	    hcl != null &&
	    ecl != null &&
	    pid != null
	)
    }
```

The code to actually count the passports is trivial:

```kotlin
fun main() {
  val count = File("input.txt").toPassportListOrNull()?.count { it.isValid() }
  print(count)
}

```

## Part 2

The second part of the problem is nothing special. Before we just had to check if the required fields were present, now we must check if the fields have correct values. That’s easy enough, we just extend the `isValid `method.

```kotlin
    val validEyeColors = setOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth")
    val hairColorRegex = Regex("""#[a-f0-9]{6}""")

    fun validHeight(height: String): Boolean {
	if (height.contains("in")) {
	    val value = height.substring(0..height.length - 3).toInt()
	    return 50 <= value && value <= 76
	} else if (height.contains("cm")) {
	    val value = height.substring(0..height.length - 3).toInt()
	    return 150 <= value && value <= 193
	} else {
	    return false
	}
    }
    
    fun isValid(): Boolean {
	return (
	    byr != null && byr in 1920..2002 &&
	    iyr != null && iyr in 2010..2020 &&
	    eyr != null && eyr in 2020..2030 &&
	    hgt != null && validHeight(hgt) &&
	    hcl != null && hairColorRegex.matches(hcl) &&
	    ecl != null && validEyeColors.contains(ecl) &&
	    pid != null && pid.length == 9
	)
    }
```

The validation code is pretty ugly but it gets the job done. The first 3 fields are years, so we just check if they are within a given range.

To validate the height, I created a separate method. Heights can be given either in centimeters or inches. It made sense to have a separate method for that.

Hair color was just a matter of using a regular expression for a HEX color code. Eye color is a set membership check. And the last field, Passport ID (PID), we just check its length.

## The end

That’s it for day 4. I’m not exactly happy with this code. I feel like I wrote too much to accomplish too little. I’m sure Kotlin has some facilities that would make writing this code a lot shorter, but I haven’t explored that yet.

Now on to day 05!