From 54a7c51e1cb2ccd143bf1658f27c167c6093ef0b Mon Sep 17 00:00:00 2001 From: Brian Peck Date: Tue, 17 Dec 2024 21:48:48 -0800 Subject: [PATCH] Day 18 2024 --- .../peckb/aoc/_2024/calendar/day18/Day18.kt | 91 +++++++++++++++++++ .../peckb/aoc/_2024/calendar/day18/README.md | 1 + .../me/peckb/aoc/_2024/TestDayComponent.kt | 2 + .../aoc/_2024/calendar/day18/Day18Test.kt | 32 +++++++ 4 files changed, 126 insertions(+) create mode 100644 src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18.kt create mode 100644 src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/README.md create mode 100644 src/test/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18Test.kt diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18.kt b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18.kt new file mode 100644 index 00000000..eb6ea845 --- /dev/null +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18.kt @@ -0,0 +1,91 @@ +package me.peckb.aoc._2024.calendar.day18 + +import javax.inject.Inject +import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory +import me.peckb.aoc.pathing.GenericIntDijkstra + +class Day18 @Inject constructor( + private val generatorFactory: InputGeneratorFactory, +) { + fun partOne(filename: String) = generatorFactory.forFile(filename).readAs(::location) { locations -> + val area = Array(HEIGHT) { Array(WIDTH) { Memory.EMPTY } } + + locations.take(1024).forEach { (y, x) -> + area[y][x] = Memory.CORRUPTED + } + + val solver = object : GenericIntDijkstra() { } + + val solutions = solver.solve( + Location(0, 0).withArea(area).withPreviousPath(emptyList()), + Location(HEIGHT - 1, WIDTH- 1) + ) + + solutions[Location(HEIGHT - 1, WIDTH- 1)] + } + + fun partTwo(filename: String) = generatorFactory.forFile(filename).readAs(::location) { locations -> + val area = Array(HEIGHT) { Array(WIDTH) { Memory.EMPTY } } + val start = Location(0, 0).withArea(area).withPreviousPath(emptyList()) + val end = Location(HEIGHT - 1, WIDTH - 1) + + val locationList = locations.toList() + + var counter = 1024 + locationList.take(1024).forEach { (y, x) -> area[y][x] = Memory.CORRUPTED } + + var solutions: Map = emptyMap() + val solver = object : GenericIntDijkstra() {} + search@ do { + val next = locationList[counter++] + area[next.y][next.x] = Memory.CORRUPTED + + if (solutions.entries.firstOrNull { it.key == end }?.key?.path?.contains(next) == false) { + continue@search + } + + solutions = solver.solve(start, end) + } while(solutions[end] != null) + + locationList[counter - 1].let { "${it.x},${it.y}" } + } + + private fun location(line: String) = line.split(",") + .map { it.toInt() } + .let { (x, y) -> Location(y, x) } + + companion object { + const val WIDTH = 71 + const val HEIGHT = 71 + } +} + +data class Location(val y: Int, val x: Int) : GenericIntDijkstra.DijkstraNode { + lateinit var path: List + lateinit var area: Array> + + fun withArea(area: Array>) = apply { this.area = area } + fun withPreviousPath(path: List) = apply { this.path = path.plus(this) } + + override fun neighbors(): Map { + return Direction.entries.mapNotNull { d -> + val (newY, newX) = d.yDelta + y to d.xDelta + x + if (newY in area.indices && newX in area[newY].indices && area[newY][newX] == Memory.EMPTY) { + Location(newY, newX).withArea(area).withPreviousPath(path) + } else { null } + }.associateWith { 1 } + } +} + +enum class Direction(val yDelta: Int, val xDelta: Int) { + N(-1, 0), + E(0, 1), + S(1, 0), + W(0, -1); +} + +enum class Memory(val s: String) { + EMPTY("."), CORRUPTED("#"); + + override fun toString(): String = s +} diff --git a/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/README.md b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/README.md new file mode 100644 index 00000000..ac9336a8 --- /dev/null +++ b/src/main/kotlin/me/peckb/aoc/_2024/calendar/day18/README.md @@ -0,0 +1 @@ +## [Day 18: RAM Run](https://adventofcode.com/2024/day/18) \ No newline at end of file diff --git a/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt b/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt index 63f881c7..763df693 100644 --- a/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt +++ b/src/test/kotlin/me/peckb/aoc/_2024/TestDayComponent.kt @@ -17,6 +17,7 @@ import me.peckb.aoc._2024.calendar.day14.Day14Test import me.peckb.aoc._2024.calendar.day15.Day15Test import me.peckb.aoc._2024.calendar.day16.Day16Test import me.peckb.aoc._2024.calendar.day17.Day17Test +import me.peckb.aoc._2024.calendar.day18.Day18Test import javax.inject.Singleton import me.peckb.aoc.DayComponent import me.peckb.aoc.InputModule @@ -42,4 +43,5 @@ internal interface TestDayComponent : DayComponent { fun inject(day15Test: Day15Test) fun inject(day16Test: Day16Test) fun inject(day17Test: Day17Test) + fun inject(day18Test: Day18Test) } diff --git a/src/test/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18Test.kt b/src/test/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18Test.kt new file mode 100644 index 00000000..412ea236 --- /dev/null +++ b/src/test/kotlin/me/peckb/aoc/_2024/calendar/day18/Day18Test.kt @@ -0,0 +1,32 @@ +package me.peckb.aoc._2024.calendar.day18 + +import javax.inject.Inject + +import me.peckb.aoc._2024.DaggerTestDayComponent +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +internal class Day18Test { + @Inject + lateinit var day18: Day18 + + @BeforeEach + fun setup() { + DaggerTestDayComponent.create().inject(this) + } + + @Test + fun testDay18PartOne() { + assertEquals(416, day18.partOne(DAY_18)) + } + + @Test + fun testDay18PartTwo() { + assertEquals("50,23", day18.partTwo(DAY_18)) + } + + companion object { + private const val DAY_18: String = "advent-of-code-input/2024/day18.input" + } +}