:warning: Day 4 Solution Thread - here be spoilers...
# advent-of-code
a
⚠️ Day 4 Solution Thread - here be spoilers ⚠️
b
!
a
Copy code
private val input = readInput("input4.txt")

fun main() {
    val all = listOf("byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid")

    input.split("\n\n").filter { passport ->
        val found =
            passport.split(" ", "\n").filter { it.isNotBlank() }.map { it.split(":")[0] to it.split(":")[1] }.toMap()
        found.keys.containsAll(all)
    }.size.let { println("Part 1: $it") }

    input.split("\n\n").filter { passport ->
        val found =
            passport.split(" ", "\n").filter { it.isNotBlank() }.map { it.split(":")[0] to it.split(":")[1] }.toMap()
        if (!found.keys.containsAll(all)) false
        else {
            found["byr"]?.toIntOrNull()?.let { it in 1920..2002 } == true &&
                    found["iyr"]?.toIntOrNull()?.let { it in 2010..2020 } == true &&
                    found["eyr"]?.toIntOrNull()?.let { it in 2020..2030 } == true &&
                    found["pid"]?.toIntOrNull()?.let { found["pid"]!!.length == 9 } == true &&
                    found["ecl"] in "amb blu brn gry grn hzl oth".split(" ") &&
                    found["hcl"]!![0] == '#'
                    && found["hcl"]!!.substring(found["hcl"]!!.length - 6).all { it in '0'..'9' || it in 'a'..'f' }
                    && if (found["hgt"]!!.endsWith("cm") || found["hgt"]!!.endsWith("in")) {
                if (found["hgt"]!!.endsWith("cm")) found["hgt"]!!.removeSuffix("cm").toInt() in 150..193
                else found["hgt"]!!.removeSuffix("in").toInt() in 59..76
            } else false
        }
    }.size.let { println("Part 2: $it") }
}
b
I'm stuck with something that pass the test but not the full input
a
want to share @bjonnh? might be able to help
b
it is ugly 😄
a
no worries lol, you can dm me if you want
it is full of me trying to find where it fails
I get part 1
but part 2 isn't ok
a
looking
b
oh I know
I didn't do validIn1 && validIn2
so of course I had some invalids with part1 rules that were not filtered out
a
ha
b
really just doing it in bash would have been much faster though
awk+sed
antlr would have been quick to write probably as well
my final version
j
My version here: https://github.com/jorispz/aoc-2020/blob/master/src/commonMain/kotlin/day04/p04.kt Quite pleased with this map of validators:
Copy code
val validators = mapOf<String, (String?) -> Boolean>(
    "cid" to { true },
    "byr" to { it?.toInt() in 1920..2002 },
    "iyr" to { it?.toInt() in 2010..2020 },
    "eyr" to { it?.toInt() in 2020..2030 },
    "hcl" to { it?.matches(Regex("""^#[0-9a-f]{6}$""")) ?: false },
    "ecl" to { it?.matches(Regex("""^amb|blu|brn|gry|grn|hzl|oth$""")) ?: false },
    "pid" to { it?.matches(Regex("""^[0-9]{9}$""")) ?: false },
    "hgt" to {
        it?.let {
            Regex("""(\d*)(cm|in)""").matchEntire(it)?.let { match ->
                val (value, unit) = match.destructured
                when (unit) {
                    "cm" -> value.toInt() in 150..193
                    "in" -> value.toInt() in 59..76
                    else -> false
                }
            }
        } ?: false
    }
)
Timings per platform:
Copy code
| Platform         | Average (ms)     | Measurements (ms) |
| -----------------| ----------------:|------------------:|
| JVM (OpenJDK 11) | 11.3±14,5        | `70, 21, 16, 17, 10, 10, 8, 11, 6, 6, 5, 5, 4, 4, 6, 5, 3, 4, 3, 2` |
| Node JS          | 14.8±13.5        | `65, 26, 35, 17, 8, 8, 9, 7, 7, 8, 8, 8, 12, 8, 8, 12, 10, 12, 10, 8` |
| Native           | 56.2±6.0         | `53, 69, 51, 51, 47, 60, 59, 47, 56, 49, 52, 56, 53, 57, 62, 65, 56, 48, 64, 57` |
n
j
Great minds think alike, and fools seldom differ 😀
🙌 1
n
Copy code
package day4

import utils.*

fun String.popSuffix(suffix: String) = if (endsWith(suffix)) removeSuffix(suffix) else null

val fields = listOf<Pair<String, (String) -> Boolean>>(
    "byr" to { it.toInt() in 1920..2002 },
    "iyr" to { it.toInt() in 2010..2020 },
    "eyr" to { it.toInt() in 2020..2030 },
    "hgt" to {
        it.popSuffix("cm")?.let { it.toInt() in 150..193 } ?: it.popSuffix("in")?.let { it.toInt() in 59..76 } ?: false
    },
    "hcl" to { it[0] == '#' && it.substring(1).all { c -> c.isDigit() || c in 'a'..'f' } },
    "ecl" to { it in listOf("amb", "blu", "brn", "gry", "grn", "hzl", "oth") },
    "pid" to { it.length == 9 && it.all { c -> c.isDigit() } })

fun getInput() = (aocDataDir / "day4.txt").readText()
    .split("\n\n")
    .map { passportString ->
        passportString
            .split(" ", "\n")
            .filter { !it.isEmpty() }
            .associate { it.split(":").let { it[0] to it[1] } }
    }

fun Map<String, String>.passportValid1() = fields.all { it.first in this }

fun part1() = getInput().count { p -> fields.all { it.first in p } }.also { println(it) }
fun part2() = getInput()
    .count { p -> fields.all { f -> p[f.first]?.let { f.second(it) } ?: false } }
    .also { println(it) }
similar to the map of validators idea though I didn't make it a map. I suppose that a map would be nice insofar as you could count passwords without storing them into memory, but after yesterday I decided to always start by reading the full data into memory 🙂
u
I'm new to kotlin and would like to receive some advices/tips, can i post my solution here?
j
Sure. It helps if you have specific questions though
u
hmm in general how can i improve or optimize it and maybe what are the more "kotlin" way to write it, idk if that makes sense. Here's my code:
Copy code
import java.io.File
import java.io.InputStream

fun main(args: Array<String>) {
    val inputStream: InputStream = File("C:\\Users\\jayso\\AppData\\Roaming\\JetBrains\\IdeaIC2020.3\\scratches\\scratch.txt").inputStream()
    val lista = mutableListOf<String>()

    inputStream.bufferedReader().forEachLine { lista.add(it) }

    var count=0
    var countInt=0
    var validI= 0

    for (riga in lista) {
        if(riga.length==0) {
            if(countInt>=7&&validI==7){
                count++
            }
            countInt=0
            validI=0
        }else{
            val temp = riga.split(" ")
            for (item in temp) {
                var listaInt= item.split(":")
                if(!listaInt[0].equals("cid")){
                    countInt++
                    when(listaInt[0]){
                        "ecl"-> if(listaInt[1].matches(Regex("amb|blu|brn|gry|grn|hzl|oth"))) validI++
                        "byr"-> if(listaInt[1].toInt().let{it in 1920..2002 }) validI++
                        "iyr"-> if(listaInt[1].toInt().let{it in 2010..2020 }) validI++
                        "eyr"-> if(listaInt[1].toInt().let{it in 2020..2030 }) validI++
                        "hgt"-> if(listaInt[1].contains("in")){
                                    if(listaInt[1].filter { it.isDigit() }.toInt().let { it in 59..76 }) validI++
                                }else{
                                    if(listaInt[1].filter { it.isDigit() }.toInt().let { it in 150..193 }) validI++
                                }
                        "hcl"-> if(listaInt[1].split("#")[0].all { it in '0'..'9' || it in 'a'..'f'}) validI++
                        "pid"-> if(listaInt[1].length==9) validI++
                    }
                }
            }
        }
    }
    println(count)
}
Should probably improve my variables naming
a
@UnAn You’re right, there is room to improve to make it more idiomatic kotlin and reads more like kotlin-y java, but that’s ok! Use File.readText() to read file’s full text (or readLines). For example, this is my function for reading in files:
Copy code
fun readInput(filename: String): String {
    return File(ClassLoader.getSystemResource(filename).file).readText()
}
Use filter/map/count Do not use .equals - kotlin will do string equality normally toInt/toX can throw exceptions if there’s a bad format, instead you might want to use toIntOrNull in case you’re trying to parse an invalid integer (then ?.let {} ? :false)
u
Thank you very much, will try to put these in the next days
a
Also, do not check if a string/list length is 0, use isEmpty Your regex with ecl seems unnecessary, you can just do ecl in listOf(“amb”,...,...) @UnAn would be happy to review more code
b
hey you know that if you use intellij we have live coding now
so you can share your session with others
a
wait we do?
whoa
b
yes we do
if you use the last version you have a feature called "code with me"
works well
j
Yeah we use it a lot these days with everyone working from home