Skip to content

Commit

Permalink
2023 - Day 17 - part 1 & 2 (Dijkstra)
Browse files Browse the repository at this point in the history
  • Loading branch information
fmmr committed Dec 17, 2023
1 parent 50e8180 commit 02db121
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/main/kotlin/no/rodland/advent_2023/Day14.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class Day14(val input: List<String>) : Day<Int, Int, List<String>> {

private fun hopOne(line: String): String {
val array = line.toCharArray()
line.forEachIndexed { idx, c ->
line.forEachIndexed { idx, _ ->
if (line[idx] == '.' && (idx + 1) < line.length && line[idx + 1] != '#') {
val tmp = line[idx]
array[idx] = line[idx + 1]
Expand Down
69 changes: 60 additions & 9 deletions src/main/kotlin/no/rodland/advent_2023/Day17.kt
Original file line number Diff line number Diff line change
@@ -1,27 +1,78 @@
package no.rodland.advent_2023

import no.rodland.advent.Day
import no.rodland.advent.Direction
import no.rodland.advent.Pos
import java.util.*

// template generated: 17/12/2023
// Fredrik Rødland 2023

class Day17(val input: List<String>) : Day<Long, Long, List<String>> {

class Day17(val input: List<String>) : Day<Int, Int, Map<Pos, Int>> {
private val parsed = input.parse()
private val width = input.first().length
private val height = input.size

override fun partOne(): Long {
return 2
override fun partOne(): Int {
return dijkstra(1) { state, nextDirection ->
state.steps < 3 || state.direction != nextDirection
}
}

override fun partTwo(): Long {
return 2
override fun partTwo(): Int {
val minToMove = 4
return dijkstra(minToMove) { state, nextDirection ->
!((state.steps > 9 && state.direction == nextDirection) || ((state.steps < minToMove) && state.direction != nextDirection))
}
}

override fun List<String>.parse(): List<String> {
return map { line ->
line
private fun dijkstra(minStepsToMove: Int = 1, isValidNextMove: (State, Direction) -> Boolean): Int {
val goal = Pos(width - 1, height - 1)
val seen = mutableSetOf<State>()
val queue = PriorityQueue(heatLossComparator)

State(Pos(0, 0), Direction.EAST, 0).apply {
queue += this to 0
seen += this
}

while (queue.isNotEmpty()) {
val (state, heatLoss) = queue.poll()
if (state.pos == goal && state.steps >= minStepsToMove) return heatLoss

Direction.entries
.asSequence()
.filterNot { d -> state.direction.goBack() == d }
.filter { direction -> isValidNextMove(state, direction) }
.map { direction -> state.next(direction) }
.filter { newState -> newState.pos in parsed }
.filterNot { newState -> newState in seen }
.forEach { newState ->
queue += newState to heatLoss + parsed[newState.pos]!!
seen += newState
}
}
throw IllegalStateException("No route to goal")
}

operator fun Array<Array<Char>>.contains(pos: Pos): Boolean = pos.x in 0..<width && pos.y in 0..<height
operator fun Array<Array<Char>>.get(pos: Pos): Char = this[pos.y][pos.x]

private val heatLossComparator: Comparator<Pair<State, Int>> = compareBy { it.second }

private data class State(val pos: Pos, val direction: Direction, val steps: Int) {
fun next(nextDirection: Direction): State =
State(pos.next(nextDirection), nextDirection, if (direction == nextDirection) steps + 1 else 1)
}

override fun List<String>.parse(): Map<Pos, Int> {
return flatMapIndexed { y, line ->
line.mapIndexed { x, c ->
Pos(x, y) to c.digitToInt()
}
}.toMap()
}

override val day = "17".toInt()
}

37 changes: 33 additions & 4 deletions src/test/kotlin/no/rodland/advent_2023/Day17Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ import readFile
internal class Day17Test {
private val data17 = "2023/input_17.txt".readFile()
private val test17 = "2023/input_17_test.txt".readFile()
private val test17_2 = "2023/input_17_test_2.txt".readFile()

private val resultTestOne = 2L
private val resultTestTwo = 2L
private val resultOne = 2L
private val resultTwo = 2L
private val resultTestOne = 102
private val resultTestTwo = 94
private val resultTestTwoTwo = 71
private val resultOne = 791
private val resultTwo = 900

val test = defaultTestSuiteParseOnInit(
Day17(data17),
Expand All @@ -29,6 +31,8 @@ internal class Day17Test {
resultTwo,
{ Day17(data17) },
{ Day17(test17) },
numTestPart1 = 1,
numTestPart2 = 1,
)

@Nested
Expand All @@ -54,6 +58,25 @@ internal class Day17Test {
}
}

@Nested
inner class Tests {
@Test
fun `17,1,test`() {
report(test.testPart1)
}

@Test
fun `17,2,test`() {
report(test.testPart2)
}

@Test
fun `17,2,test,2`() {
report(test.testPart2.copy(function = { Day17(test17_2).partTwo() }, expected = resultTestTwoTwo))
}

}

@Nested
inner class `Part 1` {
@Test
Expand All @@ -75,6 +98,12 @@ internal class Day17Test {
}

@Test
fun `17,2,test,2`() {
report(test.testPart2.copy(function = { Day17(test17_2).partTwo() }, expected = 71))
}

@Test
@Slow(888)
fun `17,2,live,1`() {
report(test.livePart2)
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/resources/2023/input_17_test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
4564679986453
1224686865563
2546548887735
4322674655533
4322674655533
5 changes: 5 additions & 0 deletions src/test/resources/2023/input_17_test_2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
111111111111
999999999991
999999999991
999999999991
999999999991

0 comments on commit 02db121

Please sign in to comment.