-
Notifications
You must be signed in to change notification settings - Fork 0
/
Day22.kt
154 lines (133 loc) Β· 5.72 KB
/
Day22.kt
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package y2023.day22
import AocPuzzle
import de.fabmax.kool.KoolApplication
import de.fabmax.kool.math.*
import de.fabmax.kool.math.spatial.BoundingBoxD
import de.fabmax.kool.modules.ksl.KslLitShader
import de.fabmax.kool.modules.ksl.KslPbrShader
import de.fabmax.kool.pipeline.ao.AoPipeline
import de.fabmax.kool.scene.addColorMesh
import de.fabmax.kool.scene.orbitCamera
import de.fabmax.kool.scene.scene
import de.fabmax.kool.util.MdColor
import de.fabmax.kool.util.Time
import kotlin.concurrent.thread
import kotlin.math.abs
import kotlin.math.min
fun main() = Day22.runAll()
object Day22 : AocPuzzle<Int, Int>() {
override fun solve1(input: List<String>): Int {
val brickMap = settleBricks(input)
if (!isTestRun()) {
drawBricks(brickMap)
}
val bricks = brickMap.values.distinct()
return bricks.count { brick -> brick.isSafeToRemove(brickMap) }
}
override fun solve2(input: List<String>): Int {
val brickMap = settleBricks(input)
val bricks = brickMap.values.distinct()
return bricks.sumOf { (it.collectFalling(brickMap, mutableSetOf(it)) - it).size }
}
private fun Brick.collectFalling(brickMap: Map<Vec3i, Brick>, result: MutableSet<Brick>): Set<Brick> {
val tops = topBricks(brickMap).filter { it !in result }
tops.filter { it.bottomBricks(brickMap).all { sup -> sup in result } }.forEach { result += it }
tops.filter { it in result }.forEach { it.collectFalling(brickMap, result) }
return result
}
private fun settleBricks(input: List<String>): Map<Vec3i, Brick> {
val bricks = input.mapIndexed { i, it ->
it.split("~").let { (start, end) -> Brick(i, Vec3i(start), Vec3i(end)) }
}
val brickMap = mutableMapOf<Vec3i, Brick>()
bricks.sortedBy { it.bottom }.forEach { b ->
var brick = b
while (brick.bottom > 1 && brick.points.map { it - Vec3i.Z_AXIS }.count { it in brickMap.keys } == 0) {
brick = brick.moveDown()
}
brick.points.forEach { brickMap[it] = brick }
}
return brickMap
}
fun Vec3i(str: String): Vec3i {
val (x, y, z) = str.split(",").map { it.toInt() }
return Vec3i(x, y, z)
}
data class Brick(val id: Int, val start: Vec3i, val end: Vec3i) {
val xPoints: List<Vec3i>
get() = (start.x..end.x).map { Vec3i(it, start.y, start.z) }
val yPoints: List<Vec3i>
get() = (start.y..end.y).map { Vec3i(start.x, it, start.z) }
val zPoints: List<Vec3i>
get() = (start.z..end.z).map { Vec3i(start.x, start.y, it) }
val points: Set<Vec3i>
get() = xPoints.toSet() + yPoints + zPoints
val bottom: Int
get() = min(start.z, end.z)
fun moveDown() = copy(start = start - Vec3i.Z_AXIS, end = end - Vec3i.Z_AXIS)
fun topBricks(brickMap: Map<Vec3i, Brick>): List<Brick> {
return points
.map { it + Vec3i.Z_AXIS }
.mapNotNull { brickMap[it] }
.distinct()
.filter { it != this }
}
fun bottomBricks(brickMap: Map<Vec3i, Brick>): List<Brick> {
return points
.map { it - Vec3i.Z_AXIS }
.mapNotNull { brickMap[it] }
.distinct()
.filter { it != this }
}
fun isSafeToRemove(brickMap: Map<Vec3i, Brick>): Boolean {
return topBricks(brickMap).all { supportedBrick -> supportedBrick.bottomBricks(brickMap).size > 1}
}
override fun toString(): String {
return if (id < 26) "${'A' + id}" else "$id"
}
}
@Suppress("unused")
fun drawBricks(brickMap: Map<Vec3i, Brick>) = thread {
KoolApplication {
it.scenes += scene {
tryEnableInfiniteDepth()
mainRenderPass.clearColor = MdColor.GREY tone 300
orbitCamera {
maxZoom = 500.0
zoom = 60.0
camera.setClipRange(0.1f, 1000f)
translationBounds = BoundingBoxD(Vec3d(0.0, 0.0, -10.0), Vec3d(10.0, 500.0, 00.0))
setMouseTranslation(0f, 30f, 0f)
setMouseRotation(0f, 52f)
}
val ao = AoPipeline.createForward(this)
addColorMesh {
generate {
// z up
rotate(90f.deg, Vec3f.NEG_X_AXIS)
translate(-5f, -5f, 0f)
brickMap.values.distinct().forEach { brick ->
val c = if (brick.isSafeToRemove(brickMap)) MdColor.LIGHT_GREEN.toLinear() else MdColor.RED.toLinear()
color = c.toOklab().shiftLightness(randomF(-0.3f, 0.3f)).toLinearRgb()
cube {
val s = brick.start.toVec3f()
val e = brick.end.toVec3f()
origin.set((s + e) * 0.5f + Vec3f(0.5f))
size.set(abs(e.x - s.x) + 1f, abs(e.y - s.y) + 1f, abs(e.z - s.z) + 1f)
size -= Vec3f(0.1f)
}
}
shader = KslPbrShader {
color { vertexColor() }
ao { enableSsao(ao.aoMap) }
ambientColor = KslLitShader.AmbientColor.Uniform(MdColor.GREY toneLin 300)
}
}
onUpdate {
transform.rotate(15f.deg * Time.deltaT, Vec3f.Y_AXIS)
}
}
}
}
}
}