Skip to content

Commit

Permalink
Solution for 2023-12-22, part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
pkoziol committed Dec 22, 2023
1 parent dce4075 commit d471608
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 38 deletions.
111 changes: 73 additions & 38 deletions src/main/kotlin/biz/koziolek/adventofcode/year2023/day22/day22.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ fun main() {
val inputFile = findInput(object {})
val bricks = parseBricks(inputFile.bufferedReader().readLines())
println("${findSafeToDisintegrate(bricks).size} are safe to disintegrate")
println("${countBricksThatWouldFall(bricks).values.sum()} other bricks would fall when removing each brick")
}

data class Brick(val from: Coord3d, val to: Coord3d) {
Expand Down Expand Up @@ -52,45 +53,10 @@ fun parseBricks(lines: Iterable<String>): List<Brick> =
)
}

fun findSafeToDisintegrate(bricks: Collection<Brick>, debug: Boolean = false): Set<StationaryBrick> {
fun findSafeToDisintegrate(bricks: Collection<Brick>): Set<StationaryBrick> {
val fallen = fallAll(bricks)
val supportingMap = fallen.associateWith { getSupportedBricks(it, fallen) }
val supportedByMap = supportingMap.entries
.flatMap { supporting ->
supporting.value.map { supported ->
supported to supporting.key
}
}
.groupBy { it.first }
.mapValues { entry -> entry.value.map { it.second } }

if (debug) {
println("Supporting map:")
supportingMap.forEach { (supporting, supportedList) ->
println(" $supporting a.k.a. ${getFriendlyName(supporting)} supports:")
supportedList.forEach { supported ->
println(" $supported a.k.a. ${getFriendlyName(supported)}")
}
println()
}

println("Supported by map:")
supportedByMap.forEach { (supported, supportingList) ->
println(" $supported a.k.a. ${getFriendlyName(supported)} is supported by:")
supportingList.forEach { supporting ->
println(" $supporting a.k.a. ${getFriendlyName(supporting)}")
}
println()
}
}

return supportingMap
.filter { supporting ->
supporting.value.all { supported ->
supportedByMap[supported]!!.size > 1
}
}
.keys
val (supportingMap, supportedByMap) = buildSupportMaps(fallen)
return fallen.filter { isSafeToDisintegrate(it, supportingMap, supportedByMap) }.toSet()
}

fun fallAll(bricks: Collection<Brick>): List<StationaryBrick> {
Expand Down Expand Up @@ -125,6 +91,75 @@ fun fallAll(bricks: Collection<Brick>): List<StationaryBrick> {
fun getSupportedBricks(brick: StationaryBrick, bricks: Collection<StationaryBrick>): List<StationaryBrick> =
bricks.filter { it.fallen.bottomZ == brick.fallen.topZ + 1 && it.fallen.crossSection.overlaps(brick.fallen.crossSection) }

fun countBricksThatWouldFall(bricks: Collection<Brick>): Map<StationaryBrick, Int> {
val fallen = fallAll(bricks)
val sortedByZ = fallen.sortedByDescending { it.fallen.bottomZ }
val (supportingMap, supportedByMap) = buildSupportMaps(fallen)
val counts = mutableMapOf<StationaryBrick, Int>()

for (brick in sortedByZ) {
val disintegrated = mutableSetOf(brick)
val toCheck = mutableListOf(brick)

while (toCheck.isNotEmpty()) {
val b = toCheck.removeFirst()
val orphans = supportingMap[b]!!
.filter { supported ->
supportedByMap[supported]!!.all { it in disintegrated }
}

disintegrated.addAll(orphans)
toCheck.addAll(orphans)
}

counts[brick] = disintegrated.size - 1
}

return counts
}

private fun buildSupportMaps(fallen: List<StationaryBrick>, debug: Boolean = false):
Pair<Map<StationaryBrick, List<StationaryBrick>>, Map<StationaryBrick, List<StationaryBrick>>> {
val supportingMap = fallen.associateWith { getSupportedBricks(it, fallen) }
val supportedByMap = supportingMap.entries
.flatMap { supporting ->
supporting.value.map { supported ->
supported to supporting.key
}
}
.groupBy { it.first }
.mapValues { entry -> entry.value.map { it.second } }

if (debug) {
println("Supporting map:")
supportingMap.forEach { (supporting, supportedList) ->
println(" $supporting a.k.a. ${getFriendlyName(supporting)} supports:")
supportedList.forEach { supported ->
println(" $supported a.k.a. ${getFriendlyName(supported)}")
}
println()
}

println("Supported by map:")
supportedByMap.forEach { (supported, supportingList) ->
println(" $supported a.k.a. ${getFriendlyName(supported)} is supported by:")
supportingList.forEach { supporting ->
println(" $supporting a.k.a. ${getFriendlyName(supporting)}")
}
println()
}
}

return supportingMap to supportedByMap
}

private fun isSafeToDisintegrate(brick: StationaryBrick,
supportingMap: Map<StationaryBrick, List<StationaryBrick>>,
supportedByMap: Map<StationaryBrick, List<StationaryBrick>>): Boolean =
supportingMap[brick]!!.all { supported ->
supportedByMap[supported]!!.size > 1
}

internal fun getFriendlyName(stationaryBrick: StationaryBrick): String =
when (stationaryBrick.original) {
Brick(from = Coord3d(1, 0, 1), to = Coord3d(1, 2, 1)) -> "A"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,35 @@ internal class Day22Test {
val bricks = parseBricks(input)
assertEquals(465, findSafeToDisintegrate(bricks).size)
}

@Test
fun testSampleAnswer2() {
val bricks = parseBricks(sampleInput)
val bricksThatWouldFall = countBricksThatWouldFall(bricks)

assertEquals(
mapOf(
Brick(from = Coord3d(1, 0, 1), to = Coord3d(1, 2, 1)) to 6,
Brick(from = Coord3d(0, 0, 2), to = Coord3d(2, 0, 2)) to 0,
Brick(from = Coord3d(0, 2, 3), to = Coord3d(2, 2, 3)) to 0,
Brick(from = Coord3d(0, 0, 4), to = Coord3d(0, 2, 4)) to 0,
Brick(from = Coord3d(2, 0, 5), to = Coord3d(2, 2, 5)) to 0,
Brick(from = Coord3d(0, 1, 6), to = Coord3d(2, 1, 6)) to 1,
Brick(from = Coord3d(1, 1, 8), to = Coord3d(1, 1, 9)) to 0,
),
bricksThatWouldFall.mapKeys { it.key.original }
)

assertEquals(7, bricksThatWouldFall.values.sum())
}

@Test
@Tag("answer")
fun testAnswer2() {
val input = findInput(object {}).bufferedReader().readLines()
val bricks = parseBricks(input)
val bricksThatWouldFall = countBricksThatWouldFall(bricks)
assertEquals(79042, bricksThatWouldFall.values.sum())
}
}

0 comments on commit d471608

Please sign in to comment.