1
0
Fork 0

day(22): add solution to part 2

Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
Matej Focko 2021-12-22 16:45:45 +01:00
parent c4afd4b86d
commit d93cc98f22
No known key found for this signature in database
GPG key ID: 332171FADF1DB90B
2 changed files with 70 additions and 18 deletions

View file

@ -30,3 +30,9 @@ fun readGraph(day: Int, name: String) = readInput(day, name).fold(mapOf<String,
* Converts string to md5 hash. * Converts string to md5 hash.
*/ */
fun String.md5(): String = BigInteger(1, MessageDigest.getInstance("MD5").digest(toByteArray())).toString(16) fun String.md5(): String = BigInteger(1, MessageDigest.getInstance("MD5").digest(toByteArray())).toString(16)
fun <A, B, C> product(xs: Sequence<A>, ys: Sequence<B>, zs: Sequence<C>): Sequence<Triple<A, B, C>> =
xs.flatMap { x -> ys.flatMap { y -> zs.map { z -> Triple(x, y, z) } } }
fun <A, B, C> product(xs: Iterable<A>, ys: Iterable<B>, zs: Iterable<C>): Sequence<Triple<A, B, C>> =
product(xs.asSequence(), ys.asSequence(), zs.asSequence())

View file

@ -1,10 +1,16 @@
package year2021.day22 package year2021.day22
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 = 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<IntRange> =
sequenceOf(first until other.first, other, other.last + 1..last)
.map { it.intersect(this) }
.filterNot(IntRange::isEmpty)
// endregion Global extensions
// region State // region State
enum class State { enum class State {
@ -25,25 +31,56 @@ data class Point(val x: Int, val y: Int, val z: Int) {
} }
// endregion Point // endregion Point
// region Cube // region Cuboid
data class Cube(val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) { data class Cuboid(val xRange: IntRange, val yRange: IntRange, val zRange: IntRange) {
val allPoints: Iterable<Point> val allPoints: Iterable<Point>
get() = xRange.flatMap { x -> yRange.flatMap { y -> zRange.map { z -> Point(x, y, z) } } } 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), xRange = xRange.intersect(other.xRange),
yRange = yRange.intersect(other.yRange), yRange = yRange.intersect(other.yRange),
zRange = zRange.intersect(other.zRange), zRange = zRange.intersect(other.zRange),
) )
private fun isEmpty(): Boolean = xRange.isEmpty() || yRange.isEmpty() || zRange.isEmpty()
operator fun minus(other: Cuboid): Set<Cuboid> {
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 // region Step
data class Step(val state: State, val cube: Cube) { data class Step(val state: State, val cuboid: Cuboid) {
val allPoints: Iterable<Point> val allPoints: Iterable<Point>
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 { fun String.toStep(): Step {
@ -54,12 +91,12 @@ fun String.toStep(): Step {
.map { it.split("=")[1].split("..").map(String::toInt) } .map { it.split("=")[1].split("..").map(String::toInt) }
.map { it[0]..it[1] } .map { it[0]..it[1] }
return Step(state.toState(), Cube(xRange, yRange, zRange)) return Step(state.toState(), Cuboid(xRange, yRange, zRange))
} }
// endregion Step // endregion Step
fun part1(input: List<Step>): Int = input.fold(mutableSetOf<Point>()) { turnedOn, step -> fun part1(input: List<Step>): Int = input.fold(mutableSetOf<Point>()) { 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) { when (step.state) {
State.On -> turnedOn.addAll(croppedStep.allPoints) State.On -> turnedOn.addAll(croppedStep.allPoints)
State.Off -> turnedOn.removeAll(croppedStep.allPoints) State.Off -> turnedOn.removeAll(croppedStep.allPoints)
@ -68,14 +105,23 @@ fun part1(input: List<Step>): Int = input.fold(mutableSetOf<Point>()) { turnedOn
turnedOn turnedOn
}.size }.size
fun part2(input: List<Step>): Long = input.fold(mutableSetOf<Triple<Int, Int, Int>>()) { turnedOn, step -> // region Reactor
when (step.state) { data class Reactor(val cuboids: List<Cuboid> = emptyList()) {
State.On -> turnedOn.addAll(step.allPoints.map { Triple(it.x, it.y, it.z) }) fun execute(step: Step): Reactor {
State.Off -> turnedOn.removeAll(step.allPoints.map { Triple(it.x, it.y, it.z) }) // remove current one from turned on
val withoutStep = cuboids.flatMapTo(mutableListOf()) { it - step.cuboid }
if (step.state == State.On) {
withoutStep.add(step.cuboid)
} }
turnedOn return copy(cuboids = withoutStep)
}.size.toLong() }
}
// endregion Reactor
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)