1
0
Fork 0

day(22): refactor

Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
Matej Focko 2021-12-22 17:11:08 +01:00
parent d93cc98f22
commit 431166fa49
No known key found for this signature in database
GPG key ID: 332171FADF1DB90B

View file

@ -4,12 +4,21 @@ import product
import readInput import readInput
// region Global extensions // region Global extensions
fun IntRange.intersect(other: IntRange): IntRange = maxOf(first, other.first)..minOf(last, other.last) fun IntRange.intersect(other: IntRange): IntRange =
fun IntRange.size(): Long = if (isEmpty()) 0L else last - first + 1L maxOf(first, other.first)..minOf(last, other.last)
val IntRange.size: Long
get() = if (isEmpty()) 0L else last - first + 1L
fun IntRange.splitBy(other: IntRange): Sequence<IntRange> = fun IntRange.splitBy(other: IntRange): Sequence<IntRange> =
sequenceOf(first until other.first, other, other.last + 1..last) sequenceOf(
first until other.first, other, other.last + 1..last
)
.map { it.intersect(this) } .map { it.intersect(this) }
.filterNot(IntRange::isEmpty) .filterNot(IntRange::isEmpty)
val IntRange.Companion.MAX_RANGE: IntRange
get() = Int.MIN_VALUE..Int.MAX_VALUE
// endregion Global extensions // endregion Global extensions
// region State // region State
@ -24,20 +33,12 @@ fun String.toState(): State = when (this) {
} }
// endregion State // endregion State
// region Point
data class Point(val x: Int, val y: Int, val z: Int) {
fun within(xRange: IntRange, yRange: IntRange, zRange: IntRange): Boolean =
x in xRange && y in yRange && z in zRange
}
// endregion Point
// region Cuboid // region Cuboid
data class Cuboid(val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) { data class Cuboid(
val allPoints: Iterable<Point> val xRange: IntRange, val yRange: IntRange, val zRange: IntRange
get() = xRange.flatMap { x -> yRange.flatMap { y -> zRange.map { z -> Point(x, y, z) } } } ) {
val size: Long val size: Long
get() = xRange.size() * yRange.size() * zRange.size() get() = xRange.size * yRange.size * zRange.size
fun intersect(other: Cuboid): Cuboid = Cuboid( fun intersect(other: Cuboid): Cuboid = Cuboid(
xRange = xRange.intersect(other.xRange), xRange = xRange.intersect(other.xRange),
@ -45,7 +46,13 @@ data class Cuboid(val xRange: IntRange, val yRange: IntRange, val zRange: IntRan
zRange = zRange.intersect(other.zRange), zRange = zRange.intersect(other.zRange),
) )
private fun isEmpty(): Boolean = xRange.isEmpty() || yRange.isEmpty() || zRange.isEmpty() private fun intersectedBy(other: Cuboid): Boolean =
(xRange.first in other.xRange
&& yRange.first in other.yRange
&& zRange.first in other.zRange)
fun isEmpty(): Boolean =
xRange.isEmpty() || yRange.isEmpty() || zRange.isEmpty()
operator fun minus(other: Cuboid): Set<Cuboid> { operator fun minus(other: Cuboid): Set<Cuboid> {
val overlap = this.intersect(other) val overlap = this.intersect(other)
@ -55,33 +62,26 @@ data class Cuboid(val xRange: IntRange, val yRange: IntRange, val zRange: IntRan
} else if (overlap == this) { } else if (overlap == this) {
emptySet() emptySet()
} else { } else {
buildSet { product(
for ((xs, ys, zs) in product(
xRange.splitBy(overlap.xRange), xRange.splitBy(overlap.xRange),
yRange.splitBy(overlap.yRange), yRange.splitBy(overlap.yRange),
zRange.splitBy(overlap.zRange), zRange.splitBy(overlap.zRange),
)) { )
if ( .map { (xs, ys, zs) -> Cuboid(xs, ys, zs) }
xs.first !in overlap.xRange .filter { !it.intersectedBy(overlap) }
|| ys.first !in overlap.yRange .toSet()
|| zs.first !in overlap.zRange
) {
add(Cuboid(xs, ys, zs))
}
}
} }
} }
companion object {
val MAX_CUBOID =
Cuboid(IntRange.MAX_RANGE, IntRange.MAX_RANGE, IntRange.MAX_RANGE)
} }
} }
// endregion Cuboid // endregion Cuboid
// region Step // region Step
data class Step(val state: State, val cuboid: Cuboid) { data class Step(val state: State, val cuboid: Cuboid)
val allPoints: Iterable<Point>
get() = cuboid.allPoints
fun crop(cuboid: Cuboid): Step = this.copy(cuboid = this.cuboid.intersect(cuboid))
}
fun String.toStep(): Step { fun String.toStep(): Step {
val (state, regions) = this.split(" ") val (state, regions) = this.split(" ")
@ -95,23 +95,21 @@ fun String.toStep(): Step {
} }
// endregion Step // endregion Step
fun part1(input: List<Step>): Int = input.fold(mutableSetOf<Point>()) { turnedOn, step -> // region Reactor
val croppedStep = step.crop(Cuboid(-50..50, -50..50, -50..50)) data class Reactor(
when (step.state) { val interestingCuboid: Cuboid = Cuboid.MAX_CUBOID,
State.On -> turnedOn.addAll(croppedStep.allPoints) val cuboids: List<Cuboid> = emptyList()
State.Off -> turnedOn.removeAll(croppedStep.allPoints) ) {
fun execute(step: Step): Reactor {
val affectedCuboid = interestingCuboid.intersect(step.cuboid)
if (affectedCuboid.isEmpty()) {
return this
} }
turnedOn
}.size
// region Reactor
data class Reactor(val cuboids: List<Cuboid> = emptyList()) {
fun execute(step: Step): Reactor {
// remove current one from turned on // remove current one from turned on
val withoutStep = cuboids.flatMapTo(mutableListOf()) { it - step.cuboid } val withoutStep = cuboids.flatMapTo(mutableListOf()) { it - affectedCuboid }
if (step.state == State.On) { if (step.state == State.On) {
withoutStep.add(step.cuboid) withoutStep.add(affectedCuboid)
} }
return copy(cuboids = withoutStep) return copy(cuboids = withoutStep)
@ -119,7 +117,14 @@ data class Reactor(val cuboids: List<Cuboid> = emptyList()) {
} }
// endregion Reactor // endregion Reactor
fun part2(input: List<Step>): Long = input.fold(Reactor()) { reactor, step -> val INIT_RANGE = -50..50
fun part1(input: List<Step>): Long =
input.fold(Reactor(Cuboid(INIT_RANGE, INIT_RANGE, INIT_RANGE))) { reactor, step ->
reactor.execute(step)
}.cuboids.sumOf { it.size }
fun part2(input: List<Step>): Long =
input.fold(Reactor()) { reactor, step ->
reactor.execute(step) reactor.execute(step)
}.cuboids.sumOf { it.size } }.cuboids.sumOf { it.size }
@ -128,8 +133,8 @@ fun main() {
val sample = readInput(22, "sample").map(String::toStep) val sample = readInput(22, "sample").map(String::toStep)
val input = readInput(22, "input").map(String::toStep) val input = readInput(22, "input").map(String::toStep)
check(part1(smallerSample) == 39) check(part1(smallerSample) == 39L)
check(part1(sample) == 590784) check(part1(sample) == 590784L)
println(part1(input)) println(part1(input))
val rebootSample = readInput(22, "reboot_sample").map(String::toStep) val rebootSample = readInput(22, "reboot_sample").map(String::toStep)