day(22): refactor
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
d93cc98f22
commit
431166fa49
1 changed files with 60 additions and 55 deletions
|
@ -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),
|
)
|
||||||
)) {
|
.map { (xs, ys, zs) -> Cuboid(xs, ys, zs) }
|
||||||
if (
|
.filter { !it.intersectedBy(overlap) }
|
||||||
xs.first !in overlap.xRange
|
.toSet()
|
||||||
|| ys.first !in overlap.yRange
|
|
||||||
|| 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 ->
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
turnedOn
|
|
||||||
}.size
|
|
||||||
|
|
||||||
// region Reactor
|
// region Reactor
|
||||||
data class Reactor(val cuboids: List<Cuboid> = emptyList()) {
|
data class Reactor(
|
||||||
|
val interestingCuboid: Cuboid = Cuboid.MAX_CUBOID,
|
||||||
|
val cuboids: List<Cuboid> = emptyList()
|
||||||
|
) {
|
||||||
fun execute(step: Step): Reactor {
|
fun execute(step: Step): Reactor {
|
||||||
|
val affectedCuboid = interestingCuboid.intersect(step.cuboid)
|
||||||
|
if (affectedCuboid.isEmpty()) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
// 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,17 +117,24 @@ 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
|
||||||
reactor.execute(step)
|
fun part1(input: List<Step>): Long =
|
||||||
}.cuboids.sumOf { it.size }
|
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)
|
||||||
|
}.cuboids.sumOf { it.size }
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val smallerSample = readInput(22, "smaller_sample").map(String::toStep)
|
val smallerSample = readInput(22, "smaller_sample").map(String::toStep)
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue