Day 9: Disk Fragmenter
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
You are viewing a single thread.
View all comments 1 point
Kotlin
No lists were harmed in the making of this code.
Solution
import kotlin.text.flatMapIndexed
fun main() {
fun part1(input: List<String>): Long {
val disk = parseInputDay09(input)
return disk.compactFragmented().checksum()
}
fun part2(input: List<String>): Long {
val disk = parseInputDay09(input)
return disk.blockify().compactContiguous().checksum()
}
val testInput = readInput("Day09_test")
check(part1(testInput) == 1928L)
check(part2(testInput) == 2858L)
val input = readInput("Day09")
part1(input).println()
part2(input).println()
}
fun parseInputDay09(input: List<String>): DiscretizedDisk {
var id = 0
return input[0].flatMapIndexed { index, char ->
val size = "$char".toInt()
if (index % 2 == 0) List(size) { DiskBlockElement(id) }
else (List(size) { DiskBlockElement(-1) }).also { id++ }
}
}
data class DiskBlockElement(val id: Int) // -1 id is empty
data class DiskBlock(val id: Int, val indexRange: IntRange)
typealias Disk = List<DiskBlock>
typealias DiscretizedDisk = List<DiskBlockElement>
fun DiscretizedDisk.compactFragmented(): DiscretizedDisk {
val freeSpace = count { it.id < 0 }
val onlyFiles = reversed().filter { it.id >= 0 }
var indexIntoOnlyFiles = 0
val discretizedCompacted = map { if (it.id < 0) onlyFiles[indexIntoOnlyFiles++] else it }.dropLast(freeSpace) + List(freeSpace) { DiskBlockElement(-1) }
return discretizedCompacted
}
fun Disk.compactContiguous(): DiscretizedDisk {
var (onlyFiles, spaaaaace) = (this.partition { it.id >= 0 })
onlyFiles = onlyFiles.reversed()
val emptySpacesCreatedIndexes = mutableListOf<List<Int>>()
var spaceRemaining = spaaaaace.first().indexRange.size()
val emptyBlockReplacements = spaaaaace.map { emptyBlock ->
buildList {
spaceRemaining = emptyBlock.indexRange.size()
while (spaceRemaining > 0) {
val fittingBlockIndex = onlyFiles.indexOfFirst { it.indexRange.size() <= spaceRemaining }
if (fittingBlockIndex == -1) {
add(DiskBlock(-1, (emptyBlock.indexRange.last() - spaceRemaining + 1)..emptyBlock.indexRange.last()))
break
}
val fittingBlock = onlyFiles[fittingBlockIndex]
if (fittingBlock.indexRange.first <= emptyBlock.indexRange.last) {
add(DiskBlock(-1, (emptyBlock.indexRange.last() - spaceRemaining + 1)..emptyBlock.indexRange.last()))
break
}
val newDiscretizedIndex = with(emptyBlock.indexRange.last() - spaceRemaining + 1) { this until (this + fittingBlock.indexRange.size()) }
add(fittingBlock.copy(indexRange = newDiscretizedIndex))
spaceRemaining -= fittingBlock.indexRange.size()
onlyFiles = onlyFiles.withoutElementAt(fittingBlockIndex)
emptySpacesCreatedIndexes.add(fittingBlock.indexRange.toList())
}
}
}
val replaceWithEmpty = emptySpacesCreatedIndexes.flatten()
var replacementIndex = 0
return flatMap {
if (it.id >= 0) listOf(it) else emptyBlockReplacements[replacementIndex++]
}.discretize().mapIndexed { index, blockElement -> if (index in replaceWithEmpty) DiskBlockElement(-1) else blockElement }
}
fun DiscretizedDisk.blockify(): Disk = buildList {
var blockID = this@blockify.first().id
var blockStartIndex = 0
this@blockify.forEachIndexed { index, blockElement ->
if (blockElement.id != blockID) {
add(DiskBlock(blockID, blockStartIndex until index))
blockStartIndex = index
blockID = blockElement.id
} else if (index == this@blockify.lastIndex) add(DiskBlock(blockElement.id, blockStartIndex.. this@blockify.lastIndex))
}
}
fun Disk.discretize(): DiscretizedDisk = flatMap { block -> List(block.indexRange.size()) { DiskBlockElement(block.id) } }
fun DiscretizedDisk.checksum(): Long = foldIndexed(0) { index, acc, blockElement ->
if (blockElement.id >= 0) acc + index * blockElement.id else acc
}