aboutsummaryrefslogtreecommitdiff
path: root/aoc-2021-kotlin/src/Day22.kt
blob: 687011b228bb1b2e33699e5ffdb1021511b68d64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import java.lang.Integer.max
import java.lang.Integer.min

object Day22 {
    private val IntRange.width get() = last - first + 1

    private infix fun IntRange.and(other: IntRange) =
        if (last >= other.first && other.last >= first)
            max(first, other.first)..min(last, other.last)
        else
            null

    data class Cuboid(val x: IntRange, val y: IntRange, val z: IntRange) {
        val volume get() = x.width.toLong() * y.width.toLong() * z.width.toLong()
        val cubes
            get() = sequence {
                x.forEach { xv -> y.forEach { yv -> z.forEach { zv -> yield(Pos3D(xv, yv, zv)) } } }
            }

        infix fun and(other: Cuboid): Cuboid? {
            val x = (x and other.x) ?: return null
            val y = (y and other.y) ?: return null
            val z = (z and other.z) ?: return null
            return Cuboid(x, y, z)
        }

        operator fun contains(pos: Pos3D) = pos.x in x && pos.y in y && pos.z in z
    }

    data class RebootStep(val on: Boolean, val cuboid: Cuboid) {
        companion object {
            fun readSteps(input: List<String>) = input.asSequence().map { line ->
                """(on|off) x=(-?\d+)..(-?\d+),y=(-?\d+)..(-?\d+),z=(-?\d+)..(-?\d+)"""
                    .toRegex()
                    .matchEntire(line)
                    ?.destructured
                    ?.let { (action, minX, maxX, minY, maxY, minZ, maxZ) ->
                        RebootStep(
                            when (action) {
                                "on" -> true
                                "off" -> false
                                else -> throw IllegalArgumentException()
                            },
                            Cuboid(
                                minX.toInt()..maxX.toInt(),
                                minY.toInt()..maxY.toInt(),
                                minZ.toInt()..maxZ.toInt()
                            )
                        )
                    } ?: throw IllegalArgumentException()
            }
        }
    }

    fun part1(input: List<String>) = RebootStep.readSteps(input).toList().let { steps ->
        Cuboid(-50..50, -50..50, -50..50).cubes.sumOf { pos ->
            if (steps.lastOrNull { pos in it.cuboid }?.on == true) 1L else 0L
        }
    }

    fun part2(input: List<String>): Long {
        val counts = mutableMapOf<Cuboid, Int>()

        RebootStep.readSteps(input).forEach { step ->
            val countChanges = mutableMapOf<Cuboid, Int>()

            for (key in counts.keys) {
                val overlap = (step.cuboid and key) ?: continue
                countChanges.putIfAbsent(overlap, 0)
                countChanges.merge(overlap, counts[key]!!, Int::minus)
            }

            if (step.on) countChanges.merge(step.cuboid, 1, Int::plus)

            countChanges.forEach { (key, value) -> counts.merge(key, value, Int::plus) }
        }

        return counts.map { (cuboid, count) -> cuboid.volume * count }.sum()
    }
}

fun main() {
    val testInput = readInputAsLines("Day22_test")
    check(Day22.part1(testInput) == 474140L)
    check(Day22.part2(testInput) == 2758514936282235L)

    val input = readInputAsLines("Day22")
    println(Day22.part1(input))
    println(Day22.part2(input))
}