<Advent of Code 2021 day 1> :thread:
# advent-of-code
a
k
453/240. Couldn't remember the name of
windowed
d
oh there's a windowed function? I guess that's easier than
input[i] + input[i+1] + input[i+2]
j
nah, it’s not easier, at least not for 3 elems, but it’s cooler šŸ™‚
d
but refactoring my code to use it let me leverage my part1 solve:
Copy code
return part1(input.windowed(3, 1).map{it.sum()})
šŸ‘ 2
k
refactored I have
data.windowed(3) { it.sum() }.windowed(2).count { it.isSorted() }
j
similar, just with
.count { (a, b) -> b > a }
instead of isSorted(). in case there are two exact consecutive numbers
šŸ‘ 1
d
I was wondering how to leverage
count
-- very clever!!
e
I did
input.zipWithNext().count { (prev, next) -> next > prev }
. I wonder which is more effective - zipping or windowing. (of course I
windowed(3)
for the 2nd part).
p
funny thing, I found a bug in IntelliJ while solving this šŸ˜„ https://youtrack.jetbrains.com/issue/KTIJ-20298
c
Well… I solved them but I had no idea about
windowed
- wish I had šŸ˜‚! Thanks for sharing!
m
Copy code
object Day1 : Challenge("--- Day 1: Sonar Sweep ---") {
    val parsed = input.lines().map(String::toInt)
    override fun part1() = parsed.windowed(2).count { (a, b) -> b > a }
    override fun part2() = parsed.windowed(3).windowed(2).count { (a, b) -> b.sum() > a.sum() }
}
I had been using zipwithnext and always wished there was a "zipWithNAmount" function. I always knew windowed exist but it somehow slipped my mind.
k
Just realized that these 2 work
Copy code
data.zip(data.drop(1)).count { (a, b) -> a < b } // part 1
data.zip(data.drop(3)).count { (a, b) -> a < b } // part 2
there isn't an actual need to add the values together
🤯 1
m
Here’s a solution without any windowing, zipping or adding šŸ™‚
Copy code
fun main() {
    with(generateSequence(::readLine).map { it.toInt() }.toList()) {
        println(countInc(1))
        println(countInc(3))
    }
}

private fun List<Int>.countInc(d: Int) =
    (d until size).count { this[it] > this[it - d] }
m
Part 1
Copy code
input.lines().map { it.toInt() }.windowed(2).count { it[1] > it[0] }
Part 2
Copy code
input.lines().map { it.toInt() }.windowed(4).count { it[3] > it[0] }
m
@Marcin Wisniowski Nice! Even more readable when using
first()
and
last()
šŸ™‚
Copy code
fun main() {
    with(generateSequence(::readLine).map { it.toInt() }.toList()) {
        println(countInc(2))
        println(countInc(4))
    }
}

private fun List<Int>.countInc(d: Int) =
    windowed(d).count { it.last() > it.first() }
m
Good idea! Also makes the difference between part 1 and 2 only 1 character. šŸ˜„
šŸ˜Ž 1
p
m
^ Interesting! But it’s much more code than the generic solution šŸ™‚
p
Maybe it's because I wrote it 🧌 , but I find it easier to understand
t
Day 1 code and writeup are done! Nice easy start. This is my fifth year in a row of solving these in Kotlin and writing a blog about them. I’m so happy to see so many more people in here every year! • Code • Blog
K 4
ā¤ļø 2
m
@Paul Woitaschek I tried it at first and found it less clean, because I either have to do something else for part 2 anyway, or have 4 elements in the destructuring in part 2.
k
@todd.ginsberg thanks again for getting me into this, I hope you don’t mind i yoinked your 2020 AOC repo to scaffold up mine for 2021.
t
@Kents AWESOME! Yeah, clone away. Glad you’re here!
šŸ‘ 1
e
also if you'd like to take a look, my repo has GitHub Actions set up with all sorts of things: run tests on every commit/PR, run and publish docs and benchmarks on demand https://github.com/ephemient/aoc2021
šŸ‘ 1
t
Oh that’s cool. I’ve only ever set up one project with GitHub Actions (my blog) and it was a bit of a struggle. I’m going to use you example to learn from and at least run test on commit!
t
Is there a difference between
.windowed(2)
and
.zipWithNext()
?
t
Yes.
List<T>
vs.
Pair<T,T>
t
I think I prefer
.zipWithNext()
then, because we’re only supposed to be comparing 2 values.
t
I do too, but if you do have a
List<T>
with only two elements, you can use
.first()
and
.last()
, which makes it kinda nice.
t
FWIW my solution where I did my best to name things like they do in the puzzle text. I also made things more explicit by using wrapper types around Ints (that know how to compare and sum etc.)
Copy code
fun solve2(depthMeasurementInput: List<String>): Int {
    return depthMeasurementInput
        .map { it.toDepthMeasurement() }
        .toDepthMeasurementSums()
        .zipWithNext()
        .count { (prev, cur) -> prev < cur }
}
šŸ‘ 1
That’s true, hadn’t thought of that. Neat!
t
Nice work! You could make it a bit simpler by removing the
return
expression and the brackets and just return the function output directly.
Copy code
fun solve2(): Int = depthMeasurementInput.map { ... }.etc()
t
Thanks for the tip šŸ™ Here’s everything šŸ™‚ https://github.com/Sch3lp/Advent-of-Code-2021
I was thinking about mapping to an enum that defines ā€œIncreasedā€, ā€œDecreasedā€, and ā€œStayed evenā€, and then counting the ā€œIncreasedā€. But the video was nearing 1 hour and decided to stop streaming šŸ˜‰
m
Here’s a more imperative solution that also doesn’t do any windowing, zipping or summing. Quite readable, I would say?
Copy code
fun main() {
    val l = generateSequence(::readLine).map { it.toInt() }.toList()
    var part1 = 0
    var part2 = 0
    for (i in l.indices) {
        if (i > 0 && l[i] > l[i-1]) part1++
        if (i > 2 && l[i] > l[i-3]) part2++
    }
    println(part1)
    println(part2)
}
j
Those are a lot of fun solutions and I didn’t even know about
count
. I thought I’d share mine even though it’s a bit longer than most.
Copy code
fun part2(input: List<String>): Int {
    val windows = input
        .toInts()
        .windowed(3, 1)
    var previousWindowSize = windows.first().sum()

    return windows.fold(0) { total, window ->
        val windowSum = window.sum()
        val increase = if (windowSum > previousWindowSize) total + 1 else total

        previousWindowSize = windowSum

        increase
    }
}
g
Copy code
class Day01(private val entries: List<Int>) {

    companion object {
        const val input = "/adventofcode/year2021/Day01.txt"
    }

    fun part1(): Int =
        entries.countIncreasingEntries()

    fun part2(): Int =
        entries.windowed(3, 1).map { it.sum() }.countIncreasingEntries()

    private fun List<Int>.countIncreasingEntries() =
        this.zipWithNext().count { it.second > it.first }
}

fun main() {
    val entries = Day01::class.java.getResource(Day01.input)!!.readText().trim().split("\n", "\r\n").map { it.toInt() }
    val day01 = Day01(entries)
    println("Day01::part1 = ${day01.part1()}")
    println("Day01::part2 = ${day01.part2()}")
}
Copy code
import org.junit.Assert
import org.junit.Test
import kotlin.system.measureNanoTime

class Day01Test {

    private val measurements = listOf(199, 200, 208, 210, 200, 207, 240, 269, 260, 263)

    private val test = mapOf(
        Day01::part1 to (measurements to 7),
        Day01::part2 to (measurements to 5)
    )

    @Test
    fun testMySolution() {
        for (function in test.keys) {
            val data = test[function]!!
            val result: Int
            val time = measureNanoTime { result = function.invoke(Day01(data.first)) }
            println("Day01::${function.name}: ${data.first} -> $result [${time}ns]")
            Assert.assertEquals(data.second, result)
        }
    }
}
And the Unit-test for my solution ...