Skip to content

Commit

Permalink
2024 - Day 16 - part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
fmmr committed Dec 16, 2024
1 parent 8d4fd32 commit 6cbed13
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 21 deletions.
68 changes: 49 additions & 19 deletions src/main/kotlin/no/rodland/advent_2024/Day16.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,79 @@ import java.util.*

// template generated: 15/12/2024
// Fredrik Rødland 2024
data class State(val pos: Pos, val dir: Direction, val cost: Int)
data class State(val pos: Pos, val dir: Direction, val cost: Int, val positions: Set<Pos>)

class Day16(val input: List<String>) : Day<Int, Int, Cave> {

private val parsed = input.parse()
private val maze = parsed

override fun partOne(): Int {
val shortestPath = dijkstra(maze)
return shortestPath
return dijkstraWithAllMinimalPaths(maze).first
}

override fun partTwo(): Int {
return 2
return dijkstraWithAllMinimalPaths(maze).second.flatMap { it.positions }.toSet().size
}

private fun dijkstra(maze: Cave): Int {
private fun dijkstraWithAllMinimalPaths(maze: Cave): Pair<Int, MutableList<State>> {
val start = Pos(1, maze.size - 2) // Starting position (S)
val end = Pos(maze[0].size - 2, 1) // Ending position (E)

val pq = PriorityQueue<State>(compareBy { it.cost })
val visited = mutableSetOf<Pair<Pos, Direction>>() // Track visited states by Pos and Direction

pq.add(State(start, Direction.EAST, 0))
val costMap = mutableMapOf<Pair<Pos, Direction>, Int>()
val endStates = mutableListOf<State>() // Collect all end states

var minCost = Int.MAX_VALUE
pq.add(State(start, Direction.EAST, 0, setOf(start)))
while (pq.isNotEmpty()) {
val current = pq.poll()
if (!visited.add(Pair(current.pos, current.dir))) continue
if (current.pos == end) return current.cost

val move = current.pos.next(current.dir)
if (move in maze && maze[move] != '#') {
pq.add(State(move, current.dir, current.cost + 1))
// If this state exceeds the known minimum cost, skip it
if (current.cost > minCost) continue

// Allow revisiting if the cost is equal to the minimum
val key = current.pos to current.dir

// if we have an entry in the cache for this (pos,dir) which is STRICTLY lower we don't have to
// keep adding it. If it's equal or null - we should handle it.
if (costMap[key]?.let { it < current.cost } == true) continue
costMap[key] = current.cost

// Add positions to current state's path
val newPositions = current.positions + current.pos

// If we reached the end, check and update the minimum cost
if (current.pos == end) {
minCost = current.cost
endStates.add(current.copy(positions = newPositions))
continue
}

// Generate next states
val nextPos = current.pos.next(current.dir)
val left = current.dir.left()
val nextLeft = current.pos.next(left)
val right = current.dir.right()
val nextRight = current.pos.next(right)

if (inMazeAndNotWall(nextPos, maze)) {
pq.add(State(nextPos, current.dir, current.cost + 1, newPositions))
}
if (inMazeAndNotWall(nextLeft, maze)) {
pq.add(State(nextLeft, left, current.cost + 1001, newPositions))
}
if (inMazeAndNotWall(nextRight, maze)) {
pq.add(State(nextRight, right, current.cost + 1001, newPositions))
}
pq.add(State(current.pos, current.dir.left(), current.cost + 1000))
pq.add(State(current.pos, current.dir.right(), current.cost + 1000))
}
return -1 // If no path is found
return minCost to endStates
}

override fun List<String>.parse(): Cave {
return this.toCave()
}
private fun inMazeAndNotWall(nextPos: Pos, maze: Cave) = nextPos in maze && maze[nextPos] != '#'


override fun List<String>.parse() = this.toCave()

override val day = "16".toInt()
}
Expand Down
6 changes: 4 additions & 2 deletions src/test/kotlin/no/rodland/advent_2024/Day16Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ internal class Day16Test {
private val test16 = "2024/input_16_test.txt".readFile()

private val resultTestOne = 7036
private val resultTestTwo = 2
private val resultTestTwo = 45
private val resultOne = 99460
private val resultTwo = 2
private val resultTwo = 500

val test = defaultTestSuiteParseOnInit(
Day16(data16),
Expand Down Expand Up @@ -64,6 +64,7 @@ internal class Day16Test {
}

@Test
@Slow(450)
fun `16,1,live,1`() {
report(test.livePart1)
}
Expand All @@ -77,6 +78,7 @@ internal class Day16Test {
}

@Test
@Slow(400)
fun `16,2,live,1`() {
report(test.livePart2)
}
Expand Down

0 comments on commit 6cbed13

Please sign in to comment.