diff --git a/src/aoc2024/Day09.kt b/src/aoc2024/Day09.kt new file mode 100644 index 0000000..ada61cc --- /dev/null +++ b/src/aoc2024/Day09.kt @@ -0,0 +1,117 @@ +package aoc2024 + +import println +import readInput + +/* +--- Day 9: Disk Fragmenter --- +https://adventofcode.com/2024/day/9 +*/ +fun main() { + + val inlineTestInput = """2333133121414131402""" + + fun part1(input: List): Long { + val diskSize = input[0].sumOf { it - '0' } + val bitmap = IntArray(diskSize) { -1 } + var pos = 0 + var isFile = true + var fileId = 0 + var usedBlocks = 0 + for (c in input[0]) { + val size = c - '0' + if (isFile) { + usedBlocks += size + for (p in 1..size) { + bitmap[pos++] = fileId + } + fileId++ + } else { + pos += size + } + isFile = !isFile + } + var backPos = diskSize - 1 + for (i in 0 until usedBlocks) { + if (bitmap[i] == -1) { + while (bitmap[backPos] < 0) { + backPos-- + } + bitmap[i] = bitmap[backPos] + bitmap[backPos--] = -1 + } + } + return bitmap.take(usedBlocks).map(Int::toLong).reduceIndexed { index, acc, i -> acc + index * i } + } + + fun part2(input: List): Long { + val diskSize = input[0].sumOf { it - '0' } + val bitmap = IntArray(diskSize) { 0 } + var pos = 0 + var isFile = true + var fileId = 0 + val freeList = HashMap>() + val fileList = ArrayList>>() + val newFileList = ArrayList>>() + for (c in input[0]) { + val size = c - '0' + if (isFile) { + if (size == 0) println("Narf!") + fileList.add(fileId to (pos to size)) + fileId++ + } else { + if (size > 0) { + freeList.getOrPut(size) { ArrayDeque(0) }.add(pos) + } + } + pos += size + isFile = !isFile + } + + for (file in fileList.reversed()) { + val (filePos, size) = file.second + var bestList: MutableList? = null + var bestListSize = 0 + for (s in size..9) { + val list = freeList[s] + if (list?.isNotEmpty() == true && list[0] < filePos) { + if (bestList == null || list[0] < bestList[0]) { + bestList = list + bestListSize = s + } + } + } + if (bestList != null) { + val newPos = bestList.removeAt(0) + if (bestListSize > size) { + val remBucket = freeList.getOrPut(bestListSize - size) { ArrayDeque(0) } + remBucket.add(newPos + size) + remBucket.sort() + } + newFileList.add(file.first to (newPos to size)) + } else { + newFileList.add(file) + } + } + for (file in newFileList) { + for (i in 0 until file.second.second) { + bitmap[i + file.second.first] = file.first + } + } + return bitmap.map(Int::toLong).reduceIndexed { index, acc, i -> acc + index * i } + } + + // test if implementation meets criteria from the description, like: + val testInput = inlineTestInput.trim().reader().readLines() + //val testInput = readInput("aoc2024/Day09_test") + val testInputPart1Result = part1(testInput) + println("Part 1 Test: $testInputPart1Result") + val testInputPart2Result = part2(testInput) + println("Part 2 Test: $testInputPart2Result") + check(testInputPart1Result == 1928L) + check(testInputPart2Result == 2858L) + + val input = readInput("aoc2024/Day09") + part1(input).println() + part2(input).println() +}