:warning: Day 2 Solution Thread - here be spoilers...
# advent-of-code
n
⚠️ Day 2 Solution Thread - here be spoilers ⚠️
Copy code
package day2

import utils.*

data class RuledPassword(val range: IntRange, val char: Char, val password: String)

fun String.toRuledPassword(): RuledPassword {
    val (rangeStart, rangeEnd, char, password) = split(": ", " ", "-")
    assert(char.length == 1)
    return RuledPassword(IntRange(rangeStart.toInt(), rangeEnd.toInt()), char.first(), password)
}

fun RuledPassword.isValidOne() = password.count { it == char } in range

fun RuledPassword.isValidTwo(): Boolean{
    val charPresent = { i: Int -> password.elementAtOrNull(i-1)?.let { it == char} ?: false }
    return charPresent(range.first) xor charPresent(range.endInclusive)
}

fun countValid(validator: RuledPassword.() -> Boolean) = (aocDataDir / "day2.txt").useLines { it.count {
    it.toRuledPassword().validator()
}
}

fun part1() = println(countValid(RuledPassword::isValidOne))
fun part2() = println(countValid(RuledPassword::isValidTwo))
a
Copy code
private val input = readInput("input2.txt")

fun main() {
    var part1 = 0
    var part2 = 0

    input.split("\n").forEach { line ->
        val (rangeT, lT, password) = line.split(" ")
        val l = lT.removeSuffix(":")
        val ranges = rangeT.split("-").map { it.toInt() }
        val low = ranges[0]
        val high = ranges[1]

        val num = password.filter { it.toString() == l }.length
        if (num in low..high) part1++
        if ((password[low - 1].toString() == l) xor (password[high - 1].toString() == l)) part2++

    }
    println("Part 1: $part1")
    println("Part 2: $part2")
}
j
May I suggest making the daily solution threads stand out a bit more with some markup? At my company we name them ⚠️ Day N: XYZ - here be spoilers ⚠️ which makes them easy to spot and find
👍 1
n
Hope you don't mind my blatant copy paste 🙂
👍 1
😄 1
e
r
https://github.com/renatomrcosta/adventofcode/blob/main/src/main/kotlin/aoc2020/day2.kt I think I’m massively overcomplicating this whole thing
😂 2
n
i... cannot tell if this is intentional satire or not
j
You seem to be very focused on accurately naming things, and closely modeling your domain objects to align with 'the business'. Both important in general, but for AoC I'd say the story/domain can be safely ignored in favor of focusing on the algorithms and minimal data structures required for performance. In later puzzles especially you'll find that the enterprisey OO models just don't work well for these types of problems
n
I still want to know, without being mean, whether it's satire or not... I can totally see it going either way
👆 1
r
no, not satire, sorry
Another person accurately described this as “Defensive Programming”, which I guess it is true. I’ll try getting into the idea of streamlining the solutions from here on out. (not that writing this one was an undertaking or anything, but I did go overboard on modelling domain and being prepared for the eventual changes)
n
Yeah, I mean this reminds me a lot of the very over the top OO Java code from the late 90's
The problem is basically you write your codebase entirely in this ultra defensive style, now you have quadrupled your whole codebase's size and made things very hard to read because of all the indirection. And it comes in useful maybe 10% of the time, or even less.
And, even this so called "defensive" programming doesn't work great because the new code is still fragile to some changing requirements. It's robust against some changes yes but fragile against others, so often when you need to make changes, it won't save you from significant refactorings.
r
It did work pretty well for the jump between parts 1 and 2, and it is easy to test in pieces, Participating here made it clearer to me the correct strat for these sort of events / competitions. Dropping the abstractions and following the requirements to the letter probably will make me faster in the next days of the event
j
For me, a big part of the fun of aoc is to compare all kinds of solutions, try to understand what people's thought processes were, and have some discussion about pros and cons. So just write whatever you like and let them howl 😎
n
I'm curious mostly for when the algorithms needed will really become non-trivial to get good enough performance
j
Yeah that's a fair point. I know in previous years my coding style starts getting into performance problems with puzzles where the choosing the proper data structure and algorithm becomes crucial for reaching acceptable performance.
n
even the first two days for me have been a big reminder for me of why you need mutable data structures
I was looking at using groupBy and things like that and they return Map, but in the next step I'd often have a need for a MutableMap to do something more efficiently
e
.groupByTo(mutableMapOf()) { ... }
has the same result as
.groupBy { ... }.toMutableMap()
but with one fewer intermediate data structure
n
@ephemient that's still pretty inefficient if you just want the counts of each group. Ultimately just counting occurrences into a MutableMap<T, Int>, it's actually simpler to just write the code yourself than try to use the standard library and wonder if it's efficient
e
if you wanted counts, you'd use
.groupingBy { ... }.eachCount()
or
.groupingBy { ... }.eachCountTo(...)
when I run this with JMH,
Copy code
Benchmark                         Mode    Cnt  Score   Error  Units
MapBench.manual                 sample  12505  0.399 ± 0.003  ms/op
MapBench.stdlib                 sample  12769  0.391 ± 0.002  ms/op
it shows stdlib marginally outperforming the obvious hand-written loop
n
Eh I'd still prefer the hand written version in this case 🙂
1