|
| 1 | +package me.peckb.aoc._2024.calendar.day18 |
| 2 | + |
| 3 | +import javax.inject.Inject |
| 4 | +import me.peckb.aoc.generators.InputGenerator.InputGeneratorFactory |
| 5 | +import me.peckb.aoc.pathing.GenericIntDijkstra |
| 6 | + |
| 7 | +class Day18 @Inject constructor( |
| 8 | + private val generatorFactory: InputGeneratorFactory, |
| 9 | +) { |
| 10 | + fun partOne(filename: String) = generatorFactory.forFile(filename).readAs(::location) { locations -> |
| 11 | + val area = Array(HEIGHT) { Array(WIDTH) { Memory.EMPTY } } |
| 12 | + |
| 13 | + locations.take(1024).forEach { (y, x) -> |
| 14 | + area[y][x] = Memory.CORRUPTED |
| 15 | + } |
| 16 | + |
| 17 | + val solver = object : GenericIntDijkstra<Location>() { } |
| 18 | + |
| 19 | + val solutions = solver.solve( |
| 20 | + Location(0, 0).withArea(area).withPreviousPath(emptyList()), |
| 21 | + Location(HEIGHT - 1, WIDTH- 1) |
| 22 | + ) |
| 23 | + |
| 24 | + solutions[Location(HEIGHT - 1, WIDTH- 1)] |
| 25 | + } |
| 26 | + |
| 27 | + fun partTwo(filename: String) = generatorFactory.forFile(filename).readAs(::location) { locations -> |
| 28 | + val area = Array(HEIGHT) { Array(WIDTH) { Memory.EMPTY } } |
| 29 | + val start = Location(0, 0).withArea(area).withPreviousPath(emptyList()) |
| 30 | + val end = Location(HEIGHT - 1, WIDTH - 1) |
| 31 | + |
| 32 | + val locationList = locations.toList() |
| 33 | + |
| 34 | + var counter = 1024 |
| 35 | + locationList.take(1024).forEach { (y, x) -> area[y][x] = Memory.CORRUPTED } |
| 36 | + |
| 37 | + var solutions: Map<Location, Int> = emptyMap() |
| 38 | + val solver = object : GenericIntDijkstra<Location>() {} |
| 39 | + search@ do { |
| 40 | + val next = locationList[counter++] |
| 41 | + area[next.y][next.x] = Memory.CORRUPTED |
| 42 | + |
| 43 | + if (solutions.entries.firstOrNull { it.key == end }?.key?.path?.contains(next) == false) { |
| 44 | + continue@search |
| 45 | + } |
| 46 | + |
| 47 | + solutions = solver.solve(start, end) |
| 48 | + } while(solutions[end] != null) |
| 49 | + |
| 50 | + locationList[counter - 1].let { "${it.x},${it.y}" } |
| 51 | + } |
| 52 | + |
| 53 | + private fun location(line: String) = line.split(",") |
| 54 | + .map { it.toInt() } |
| 55 | + .let { (x, y) -> Location(y, x) } |
| 56 | + |
| 57 | + companion object { |
| 58 | + const val WIDTH = 71 |
| 59 | + const val HEIGHT = 71 |
| 60 | + } |
| 61 | +} |
| 62 | + |
| 63 | +data class Location(val y: Int, val x: Int) : GenericIntDijkstra.DijkstraNode<Location> { |
| 64 | + lateinit var path: List<Location> |
| 65 | + lateinit var area: Array<Array<Memory>> |
| 66 | + |
| 67 | + fun withArea(area: Array<Array<Memory>>) = apply { this.area = area } |
| 68 | + fun withPreviousPath(path: List<Location>) = apply { this.path = path.plus(this) } |
| 69 | + |
| 70 | + override fun neighbors(): Map<Location, Int> { |
| 71 | + return Direction.entries.mapNotNull { d -> |
| 72 | + val (newY, newX) = d.yDelta + y to d.xDelta + x |
| 73 | + if (newY in area.indices && newX in area[newY].indices && area[newY][newX] == Memory.EMPTY) { |
| 74 | + Location(newY, newX).withArea(area).withPreviousPath(path) |
| 75 | + } else { null } |
| 76 | + }.associateWith { 1 } |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +enum class Direction(val yDelta: Int, val xDelta: Int) { |
| 81 | + N(-1, 0), |
| 82 | + E(0, 1), |
| 83 | + S(1, 0), |
| 84 | + W(0, -1); |
| 85 | +} |
| 86 | + |
| 87 | +enum class Memory(val s: String) { |
| 88 | + EMPTY("."), CORRUPTED("#"); |
| 89 | + |
| 90 | + override fun toString(): String = s |
| 91 | +} |
0 commit comments