diff --git a/src/Utils.kt b/src/Utils.kt index 82e2236..dd81e6e 100644 --- a/src/Utils.kt +++ b/src/Utils.kt @@ -30,3 +30,9 @@ fun readGraph(day: Int, name: String) = readInput(day, name).fold(mapOf product(xs: Sequence, ys: Sequence, zs: Sequence): Sequence> = + xs.flatMap { x -> ys.flatMap { y -> zs.map { z -> Triple(x, y, z) } } } + +fun product(xs: Iterable, ys: Iterable, zs: Iterable): Sequence> = + product(xs.asSequence(), ys.asSequence(), zs.asSequence()) diff --git a/src/year2021/day22/Day22.kt b/src/year2021/day22/Day22.kt index 1744c47..e8e6073 100644 --- a/src/year2021/day22/Day22.kt +++ b/src/year2021/day22/Day22.kt @@ -1,10 +1,16 @@ package year2021.day22 +import product import readInput -// region global extensions +// region Global extensions fun IntRange.intersect(other: IntRange): IntRange = maxOf(first, other.first)..minOf(last, other.last) -// endregion global extensions +fun IntRange.size(): Long = if (isEmpty()) 0L else last - first + 1L +fun IntRange.splitBy(other: IntRange): Sequence = + sequenceOf(first until other.first, other, other.last + 1..last) + .map { it.intersect(this) } + .filterNot(IntRange::isEmpty) +// endregion Global extensions // region State enum class State { @@ -25,25 +31,56 @@ data class Point(val x: Int, val y: Int, val z: Int) { } // endregion Point -// region Cube -data class Cube(val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) { +// region Cuboid +data class Cuboid(val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) { val allPoints: Iterable get() = xRange.flatMap { x -> yRange.flatMap { y -> zRange.map { z -> Point(x, y, z) } } } - fun intersect(other: Cube): Cube = Cube( + val size: Long + get() = xRange.size() * yRange.size() * zRange.size() + + fun intersect(other: Cuboid): Cuboid = Cuboid( xRange = xRange.intersect(other.xRange), yRange = yRange.intersect(other.yRange), zRange = zRange.intersect(other.zRange), ) + + private fun isEmpty(): Boolean = xRange.isEmpty() || yRange.isEmpty() || zRange.isEmpty() + + operator fun minus(other: Cuboid): Set { + val overlap = this.intersect(other) + + return if (overlap.isEmpty()) { + setOf(this) + } else if (overlap == this) { + emptySet() + } else { + buildSet { + for ((xs, ys, zs) in product( + xRange.splitBy(overlap.xRange), + yRange.splitBy(overlap.yRange), + zRange.splitBy(overlap.zRange), + )) { + if ( + xs.first !in overlap.xRange + || ys.first !in overlap.yRange + || zs.first !in overlap.zRange + ) { + add(Cuboid(xs, ys, zs)) + } + } + } + } + } } -// endregion Cube +// endregion Cuboid // region Step -data class Step(val state: State, val cube: Cube) { +data class Step(val state: State, val cuboid: Cuboid) { val allPoints: Iterable - get() = cube.allPoints + get() = cuboid.allPoints - fun crop(cube: Cube): Step = this.copy(cube = this.cube.intersect(cube)) + fun crop(cuboid: Cuboid): Step = this.copy(cuboid = this.cuboid.intersect(cuboid)) } fun String.toStep(): Step { @@ -54,12 +91,12 @@ fun String.toStep(): Step { .map { it.split("=")[1].split("..").map(String::toInt) } .map { it[0]..it[1] } - return Step(state.toState(), Cube(xRange, yRange, zRange)) + return Step(state.toState(), Cuboid(xRange, yRange, zRange)) } // endregion Step fun part1(input: List): Int = input.fold(mutableSetOf()) { turnedOn, step -> - val croppedStep = step.crop(Cube(-50..50, -50..50, -50..50)) + val croppedStep = step.crop(Cuboid(-50..50, -50..50, -50..50)) when (step.state) { State.On -> turnedOn.addAll(croppedStep.allPoints) State.Off -> turnedOn.removeAll(croppedStep.allPoints) @@ -68,14 +105,23 @@ fun part1(input: List): Int = input.fold(mutableSetOf()) { turnedOn turnedOn }.size -fun part2(input: List): Long = input.fold(mutableSetOf>()) { turnedOn, step -> - when (step.state) { - State.On -> turnedOn.addAll(step.allPoints.map { Triple(it.x, it.y, it.z) }) - State.Off -> turnedOn.removeAll(step.allPoints.map { Triple(it.x, it.y, it.z) }) - } +// region Reactor +data class Reactor(val cuboids: List = emptyList()) { + fun execute(step: Step): Reactor { + // remove current one from turned on + val withoutStep = cuboids.flatMapTo(mutableListOf()) { it - step.cuboid } + if (step.state == State.On) { + withoutStep.add(step.cuboid) + } - turnedOn -}.size.toLong() + return copy(cuboids = withoutStep) + } +} +// endregion Reactor + +fun part2(input: List): Long = input.fold(Reactor()) { reactor, step -> + reactor.execute(step) +}.cuboids.sumOf { it.size } fun main() { val smallerSample = readInput(22, "smaller_sample").map(String::toStep)