1
+ package adventofcode2023
2
+
3
+ import java.io.File
4
+ import java.util.PriorityQueue
5
+
6
+ object Day22 {
7
+ private const val EMPTY = - 1
8
+ private const val FLOOR = 0
9
+
10
+ private val bricks = File (" resources/adventofcode2023/Day22.txt" )
11
+ .readLines()
12
+ .withIndex()
13
+ .map { brick ->
14
+ val startEnd = brick.value.split(' ~' ).map { positions ->
15
+ Vector3 (positions.split(' ,' ).map { it.toInt() })
16
+ }
17
+
18
+ Brick (brick.index + 1 , startEnd[0 ], startEnd[1 ])
19
+ }
20
+
21
+ private val map: Array <Array <Array <Int >>> =
22
+ Array (bricks.maxOf { it.end.x } + 1 ) {
23
+ Array (bricks.maxOf { it.end.y } + 1 ) {
24
+ Array (bricks.maxOf { it.end.z } + 1 ) {
25
+ if (it == 0 ) FLOOR else EMPTY
26
+ }
27
+ }
28
+ }
29
+
30
+ private fun initializeBricksMap () =
31
+ bricks.forEach { it.updateMap(true ) }
32
+
33
+ private data class Vector3 (val x : Int , val y : Int , val z : Int ) {
34
+ fun lower () = Vector3 (x, y, z - 1 )
35
+ }
36
+
37
+ private fun Vector3 (positions : List <Int >) = Vector3 (positions[0 ], positions[1 ], positions[2 ])
38
+
39
+ private data class Brick (val id : Int , var start : Vector3 , var end : Vector3 ) {
40
+ private fun iterateThroughAllCubes (fixedZ : Int = 0, onEveryCube : (Int , Int , Int ) -> Unit ) {
41
+ for (x in start.x.. end.x)
42
+ for (y in start.y.. end.y)
43
+ if (fixedZ > 0 )
44
+ onEveryCube(x, y, fixedZ)
45
+ else for (z in start.z.. end.z)
46
+ onEveryCube(x, y, z)
47
+ }
48
+
49
+ fun verticalNeighborsIds (higher : Boolean ): Set <Int > {
50
+ val supportingIds = mutableSetOf<Int >()
51
+
52
+ iterateThroughAllCubes(if (higher) end.z + 1 else start.z - 1 ) { x, y, z ->
53
+ val currentId = map[x][y][z]
54
+ if (currentId > EMPTY && ! supportingIds.contains(currentId)) supportingIds.add(currentId)
55
+ }
56
+
57
+ return supportingIds
58
+ }
59
+
60
+ private fun lower () {
61
+ start = start.lower()
62
+ end = end.lower()
63
+ }
64
+
65
+ fun updateMap (fill : Boolean ) {
66
+ iterateThroughAllCubes { x, y, z ->
67
+ map[x][y][z] = if (fill) id else - 1
68
+ }
69
+ }
70
+
71
+ fun numberOfSupporting (): Int =
72
+ verticalNeighborsIds(false ).size
73
+
74
+ fun supportedBricks (): List <Brick > =
75
+ verticalNeighborsIds(true ).map { bricks[it - 1 ] }
76
+
77
+ fun fall (): Boolean {
78
+ return if (numberOfSupporting() == 0 ) {
79
+ updateMap(false )
80
+ lower()
81
+ updateMap(true )
82
+ true
83
+ } else {
84
+ false
85
+ }
86
+ }
87
+ }
88
+
89
+ private fun makeBricksFall () {
90
+ var anyFell = true
91
+
92
+ while (anyFell) {
93
+ anyFell = false
94
+
95
+ bricks.forEach {
96
+ if (it.fall()) anyFell = true
97
+ }
98
+ }
99
+ }
100
+
101
+ private fun numberOfSafeToDisintegrate (): Int =
102
+ bricks.count { brick ->
103
+ brick.supportedBricks().all { it.numberOfSupporting() > 1 }
104
+ }
105
+
106
+ private fun numberOfBrickThatWouldFall (brick : Brick ): Int {
107
+ val currentBricks = PriorityQueue <Brick > { brick1, brick2 -> brick1.end.z - brick2.end.z }
108
+ val fallenIds = mutableListOf (brick.id)
109
+ var fallenCount = 0
110
+
111
+ currentBricks.addAll(brick.supportedBricks())
112
+
113
+ while (currentBricks.isNotEmpty()) {
114
+ val current = currentBricks.remove()
115
+
116
+ if (current.verticalNeighborsIds(false ).all { fallenIds.contains(it) }) {
117
+ if (! fallenIds.contains(current.id)) fallenIds.add(current.id)
118
+ currentBricks.addAll(current.supportedBricks().filterNot { currentBricks.contains(it) })
119
+ fallenCount++
120
+ }
121
+ }
122
+
123
+ println (" ${brick.id} : $fallenCount " )
124
+ return fallenCount
125
+ }
126
+
127
+ fun initialize () {
128
+ initializeBricksMap()
129
+ makeBricksFall()
130
+ }
131
+
132
+ fun part1 () = println (numberOfSafeToDisintegrate())
133
+
134
+ fun part2 () = println (bricks.sumOf { numberOfBrickThatWouldFall(it) })
135
+ }
136
+
137
+ fun main () {
138
+ Day22 .initialize()
139
+ Day22 .part1()
140
+ Day22 .part2()
141
+ }
0 commit comments