diff options
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | src/Day25.kt | 85 |
2 files changed, 88 insertions, 3 deletions
@@ -1,8 +1,8 @@ # Advent of Code 2021 in Kotlin  - - + + Welcome to the Advent of Code[^aoc] Kotlin project created by [tchojnacki][github] using the [Advent of Code Kotlin Template][template] delivered by JetBrains. See other solutions [here][awesome]. @@ -35,7 +35,7 @@ the [Advent of Code Kotlin Template][template] delivered by JetBrains. See other | Day 22: Reactor Reboot | | | | Day 23: Amphipod | | | | Day 24: Arithmetic Logic Unit | | | -| Day 25: Sea Cucumber | | | +| Day 25: Sea Cucumber | 🌟 | | [^aoc]: [Advent of Code][aoc] – An annual event of Christmas-oriented programming challenges started December 2015. diff --git a/src/Day25.kt b/src/Day25.kt new file mode 100644 index 0000000..309c15e --- /dev/null +++ b/src/Day25.kt @@ -0,0 +1,85 @@ +class SeaCucumbers(private val width: Int, private val height: Int, private val matrix: Array<SeaTile>) { + companion object { + fun fromLines(lines: List<String>) = + Pos2D( + lines.getOrNull(0)?.length ?: throw IllegalArgumentException("Sea must have a non-zero height!"), + lines.size + ).let { size -> + SeaCucumbers(size.x, size.y, Array(size.x * size.y) { + when (val char = lines[it / size.x][it % size.x]) { + '>' -> SeaTile.Cucumber.EastFacing + 'v' -> SeaTile.Cucumber.SouthFacing + '.' -> SeaTile.EmptyTile + else -> throw IllegalArgumentException("Found '$char', expected '>', 'v' or '.'!") + } + }) + } + } + + sealed class SeaTile { + object EmptyTile : SeaTile() + sealed class Cucumber : SeaTile() { + object EastFacing : Cucumber() + object SouthFacing : Cucumber() + } + } + + private fun moveIndex(index: Int, offset: Pos2D) = + ((index / width + offset.y) % height) * width + (index + offset.x) % width + + private inline fun <reified T : SeaTile.Cucumber> stepDirection(pos: Pos2D) { + matrix + .asSequence() + .withIndex() + .map { (index, seaTile) -> + val nextIndex = moveIndex(index, pos) + if (seaTile is T && matrix[nextIndex] is SeaTile.EmptyTile) { + index to nextIndex + } else { + null + } + } + .filterNotNull() + .toList() + .forEach { (index, nextIndex) -> + matrix[nextIndex] = matrix[index].also { matrix[index] = SeaTile.EmptyTile } + } + } + + private fun stepOnce() { + stepDirection<SeaTile.Cucumber.EastFacing>(Pos2D(1, 0)) + stepDirection<SeaTile.Cucumber.SouthFacing>(Pos2D(0, 1)) + } + + fun simulate(): Int { + var count = 0 + + do { + val previousHashCode = matrix.contentHashCode() + stepOnce() + count++ + } while (matrix.contentHashCode() != previousHashCode) + + return count + } + + override fun toString(): String = matrix + .withIndex() + .joinToString("") { (index, seaTile) -> + when (seaTile) { + SeaTile.Cucumber.EastFacing -> ">" + SeaTile.Cucumber.SouthFacing -> "v" + SeaTile.EmptyTile -> "." + } + (if (index % width == width - 1) "\n" else "") + } +} + +fun main() { + fun calculate(input: List<String>) = SeaCucumbers.fromLines(input).simulate() + + val testInput = readInputAsLines("Day25_test") + check(calculate(testInput) == 58) + + val input = readInputAsLines("Day25") + println(calculate(input)) +} |