-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay22.kt
141 lines (111 loc) · 4.09 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
package adventofcode2023
import java.io.File
import java.util.PriorityQueue
object Day22 {
private const val EMPTY = -1
private const val FLOOR = 0
private val bricks = File("resources/adventofcode2023/Day22.txt")
.readLines()
.withIndex()
.map { brick ->
val startEnd = brick.value.split('~').map { positions ->
Vector3(positions.split(',').map { it.toInt() })
}
Brick(brick.index + 1, startEnd[0], startEnd[1])
}
private val map: Array<Array<Array<Int>>> =
Array(bricks.maxOf { it.end.x } + 1) {
Array(bricks.maxOf { it.end.y } + 1) {
Array(bricks.maxOf { it.end.z } + 1) {
if (it == 0) FLOOR else EMPTY
}
}
}
private fun initializeBricksMap() =
bricks.forEach { it.updateMap(true) }
private data class Vector3(val x: Int, val y: Int, val z: Int) {
fun lower() = Vector3(x, y, z - 1)
}
private fun Vector3(positions: List<Int>) = Vector3(positions[0], positions[1], positions[2])
private data class Brick(val id: Int, var start: Vector3, var end: Vector3) {
private fun iterateThroughAllCubes(fixedZ: Int = 0, onEveryCube: (Int, Int, Int) -> Unit) {
for (x in start.x..end.x)
for (y in start.y..end.y)
if (fixedZ > 0)
onEveryCube(x, y, fixedZ)
else for (z in start.z..end.z)
onEveryCube(x, y, z)
}
fun verticalNeighborsIds(higher: Boolean): Set<Int> {
val supportingIds = mutableSetOf<Int>()
iterateThroughAllCubes(if (higher) end.z + 1 else start.z - 1) { x, y, z ->
val currentId = map[x][y][z]
if (currentId > EMPTY && !supportingIds.contains(currentId)) supportingIds.add(currentId)
}
return supportingIds
}
private fun lower() {
start = start.lower()
end = end.lower()
}
fun updateMap(fill: Boolean) {
iterateThroughAllCubes { x, y, z ->
map[x][y][z] = if (fill) id else -1
}
}
fun numberOfSupporting(): Int =
verticalNeighborsIds(false).size
fun supportedBricks(): List<Brick> =
verticalNeighborsIds(true).map { bricks[it - 1] }
fun fall(): Boolean {
return if (numberOfSupporting() == 0) {
updateMap(false)
lower()
updateMap(true)
true
} else {
false
}
}
}
private fun makeBricksFall() {
var anyFell = true
while (anyFell) {
anyFell = false
bricks.forEach {
if (it.fall()) anyFell = true
}
}
}
private fun numberOfSafeToDisintegrate(): Int =
bricks.count { brick ->
brick.supportedBricks().all { it.numberOfSupporting() > 1 }
}
private fun numberOfBrickThatWouldFall(brick: Brick): Int {
val currentBricks = PriorityQueue<Brick> { brick1, brick2 -> brick1.end.z - brick2.end.z }
val fallenIds = mutableListOf(brick.id)
var fallenCount = 0
currentBricks.addAll(brick.supportedBricks())
while (currentBricks.isNotEmpty()) {
val current = currentBricks.remove()
if (current.verticalNeighborsIds(false).all { fallenIds.contains(it) }) {
if (!fallenIds.contains(current.id)) fallenIds.add(current.id)
currentBricks.addAll(current.supportedBricks().filterNot { currentBricks.contains(it) })
fallenCount++
}
}
println("${brick.id}: $fallenCount")
return fallenCount
}
fun initialize() {
initializeBricksMap()
makeBricksFall()
}
fun part1() = println(numberOfSafeToDisintegrate())
fun part2() = println(bricks.sumOf { numberOfBrickThatWouldFall(it) })
}
fun main() {
Day22.initialize()
Day22.part1()
Day22.part2()
}