diff --git a/src/main/kotlin/de/linkel/aoc/Day14.kt b/src/main/kotlin/de/linkel/aoc/Day14.kt index 1a2181d..74a0fd3 100644 --- a/src/main/kotlin/de/linkel/aoc/Day14.kt +++ b/src/main/kotlin/de/linkel/aoc/Day14.kt @@ -2,13 +2,127 @@ package de.linkel.aoc import de.linkel.aoc.base.AbstractLinesAdventDay import de.linkel.aoc.base.QuizPart +import de.linkel.aoc.utils.grid.Area +import de.linkel.aoc.utils.grid.Grid +import de.linkel.aoc.utils.grid.Point +import de.linkel.aoc.utils.grid.Vector import jakarta.inject.Singleton +import kotlin.math.abs @Singleton class Day14: AbstractLinesAdventDay<Int>() { override val day = 14 override fun process(part: QuizPart, lines: Sequence<String>): Int { - return 0 + val grid = Grid.parse(lines) { pos, char -> + char.takeIf { it != '.' } + } + var platform = Platform( + width = grid.width, + height = grid.height, + rollers = grid.getAllData() + .filter { it.data == 'O' } + .map { it.point } + .toSet(), + blockers = grid.getAllData() + .filter { it.data == '#' } + .map { it.point } + .toSet() + ) + if (part == QuizPart.A) { + platform = platform.tilt(Platform.NORTH) + } else { + val lastIterations = mutableListOf<Int>() + var i = 0 + while (i < 1_000_000_000) { + platform = platform + .tilt(Platform.NORTH) + .tilt(Platform.WEST) + .tilt(Platform.SOUTH) + .tilt(Platform.EAST) + if (platform.rollers.hashCode() in lastIterations) { + val cycleLength = lastIterations.indexOf(platform.rollers.hashCode()) + 1 + println("in lastIterations at pos $cycleLength") + while (i + cycleLength < 1_000_000_000) { + i += cycleLength + } + println(" > fast-forward to iteration $i") + + lastIterations.clear() + } + lastIterations.add(0, platform.rollers.hashCode()) + if (lastIterations.size > 500) { + lastIterations.removeAt(500) + } + i++ + } + } + return platform.rollers + .sumOf { platform.height - it.y } + } + + data class Platform( + val width: Int, + val height: Int, + val rollers: Set<Point>, + val blockers: Set<Point> + ) { + companion object { + val NORTH = Vector(0, -1) + val WEST = Vector(-1, 0) + val SOUTH = Vector(0, 1) + val EAST = Vector(1, 0) + } + + val area = Area(0, 0, width, height) + + fun tilt(direction: Vector): Platform { + val points = rollers + .toMutableSet() + val stoppedRollers = mutableSetOf<Point>() + + val (edge, selector) = when (direction) { + NORTH -> 0 to { it: Point -> it.y } + SOUTH -> height to { it: Point -> it.y } + WEST -> 0 to { it: Point -> it.x } + EAST -> width to { it: Point -> it.x } + else -> throw Exception("invalid direction") + } + while (points.isNotEmpty()) { + points.toList() + .sortedBy { abs(edge - selector(it)) } + .forEach { pos -> + val dest = pos + direction + points.remove(pos) + if (dest in area && dest !in blockers && dest !in stoppedRollers && dest !in points) { + points.add(dest) + } else { + stoppedRollers.add(pos) + } + } + } + return copy( + rollers = stoppedRollers + ) + } + + override fun toString(): String { + return buildString { + (0 until height).forEach { y -> + (0 until width).forEach { x -> + val p = Point(x, y) + if (p in blockers) { + append('#') + } else if (p in rollers) { + append('O') + } else { + append('.') + } + } + append('\n') + } + } + } } + } diff --git a/src/main/kotlin/de/linkel/aoc/Day15.kt b/src/main/kotlin/de/linkel/aoc/Day15.kt new file mode 100644 index 0000000..ba0a633 --- /dev/null +++ b/src/main/kotlin/de/linkel/aoc/Day15.kt @@ -0,0 +1,14 @@ +package de.linkel.aoc + +import de.linkel.aoc.base.AbstractLinesAdventDay +import de.linkel.aoc.base.QuizPart +import jakarta.inject.Singleton + +@Singleton +class Day15: AbstractLinesAdventDay<Int>() { + override val day = 15 + + override fun process(part: QuizPart, lines: Sequence<String>): Int { + return 0 + } +} diff --git a/src/main/kotlin/de/linkel/aoc/Day16.kt b/src/main/kotlin/de/linkel/aoc/Day16.kt new file mode 100644 index 0000000..28c2471 --- /dev/null +++ b/src/main/kotlin/de/linkel/aoc/Day16.kt @@ -0,0 +1,14 @@ +package de.linkel.aoc + +import de.linkel.aoc.base.AbstractLinesAdventDay +import de.linkel.aoc.base.QuizPart +import jakarta.inject.Singleton + +@Singleton +class Day16: AbstractLinesAdventDay<Int>() { + override val day = 16 + + override fun process(part: QuizPart, lines: Sequence<String>): Int { + return 0 + } +} diff --git a/src/test/kotlin/de/linkel/aoc/Day14Test.kt b/src/test/kotlin/de/linkel/aoc/Day14Test.kt index c1bf181..aa27fa8 100644 --- a/src/test/kotlin/de/linkel/aoc/Day14Test.kt +++ b/src/test/kotlin/de/linkel/aoc/Day14Test.kt @@ -2,12 +2,22 @@ package de.linkel.aoc class Day14Test: AbstractDayTest<Int>() { override val exampleA = """ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... """.trimIndent() - override val exampleSolutionA = 0 - override val solutionA = 0 + override val exampleSolutionA = 136 + override val solutionA = 108889 - override val exampleSolutionB = 0 - override val solutionB = 0 + override val exampleSolutionB = 64 + override val solutionB = 104671 override val implementation = Day14() } diff --git a/src/test/kotlin/de/linkel/aoc/Day15Test.kt b/src/test/kotlin/de/linkel/aoc/Day15Test.kt new file mode 100644 index 0000000..3e5045d --- /dev/null +++ b/src/test/kotlin/de/linkel/aoc/Day15Test.kt @@ -0,0 +1,13 @@ +package de.linkel.aoc + +class Day15Test: AbstractDayTest<Int>() { + override val exampleA = """ + """.trimIndent() + override val exampleSolutionA = 0 + override val solutionA = 0 + + override val exampleSolutionB = 0 + override val solutionB = 0 + + override val implementation = Day15() +} diff --git a/src/test/kotlin/de/linkel/aoc/Day16Test.kt b/src/test/kotlin/de/linkel/aoc/Day16Test.kt new file mode 100644 index 0000000..872377b --- /dev/null +++ b/src/test/kotlin/de/linkel/aoc/Day16Test.kt @@ -0,0 +1,13 @@ +package de.linkel.aoc + +class Day16Test: AbstractDayTest<Int>() { + override val exampleA = """ + """.trimIndent() + override val exampleSolutionA = 0 + override val solutionA = 0 + + override val exampleSolutionB = 0 + override val solutionB = 0 + + override val implementation = Day16() +}