From bba6a5b5b718aee7f9625c89156c13dce7152c06 Mon Sep 17 00:00:00 2001 From: Sven Weidauer Date: Thu, 26 Dec 2024 12:24:08 +0100 Subject: [PATCH] Day 20 part 1. --- 2024/src/main/kotlin/Dijkstra.kt | 20 ++++++++- 2024/src/main/kotlin/day20.kt | 71 ++++++++++---------------------- 2 files changed, 39 insertions(+), 52 deletions(-) diff --git a/2024/src/main/kotlin/Dijkstra.kt b/2024/src/main/kotlin/Dijkstra.kt index 93e4345..6bdb1a8 100644 --- a/2024/src/main/kotlin/Dijkstra.kt +++ b/2024/src/main/kotlin/Dijkstra.kt @@ -1,7 +1,10 @@ import java.util.* -fun dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence>): Int? { +data class PathResult(val steps: List>, val totalDistance: Int) + +fun dijkstraPath(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence>): PathResult? { val distanceFromStart = mutableMapOf(start to 0) + val previous = mutableMapOf() val visited = mutableSetOf() val queue = PriorityQueue> { a, b -> a.second.compareTo(b.second) } @@ -10,7 +13,15 @@ fun dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence dijkstra(start: T, goal: (T) -> Boolean, neighbors: (T) -> Sequence>): Int? { + return dijkstraPath(start, goal, neighbors)?.totalDistance } \ No newline at end of file diff --git a/2024/src/main/kotlin/day20.kt b/2024/src/main/kotlin/day20.kt index 7c9504a..90dd795 100644 --- a/2024/src/main/kotlin/day20.kt +++ b/2024/src/main/kotlin/day20.kt @@ -1,58 +1,29 @@ fun main() { - val maze = CharGrid.read("day20-sample.txt") + val maze = CharGrid.read("day20.txt") val start = maze.find('S') ?: error("No start position") val end = maze.find('E') ?: error("No end position") - val paths = maze.findPaths(start, end) - - val total = paths.first { !it.cheated }.time + val shortest = maze.findShortestPath(start, end) ?: error("No shortest path found") + val total = shortest.last().second + val toGoal = shortest.associate { (step, fromStart) -> + step to total - fromStart + } println("Time without cheating: $total") - val part1 = paths.count { it.time <= total - 100 } - println("Part 1: $part1") - paths.sortedBy { it.time } - .groupBy { it.time } - .forEach { (time, list) -> - val saving = total - time - println("${list.count()} x $saving") - } -} - -data class MazeResult(val time: Int, val cheated: Boolean) - -fun CharGrid.findPaths( - position: Grid.Coordinate, - goal: Grid.Coordinate, - visited: Set = emptySet(), - time: Int = 0, - cheated: Boolean = false -): List { - if (position == goal) { - return listOf(MazeResult(time, cheated)) - } - - val newVisited = visited + position - - fun canVisit(position: Grid.Coordinate): Boolean = - position in this && position !in newVisited && this[position] != '#' - - val paths = position.neighbors() - .filter { canVisit(it) } - .fold(mutableListOf()) { list, next -> - list.addAll(findPaths(next, goal, newVisited, time + 1, cheated)) - list - } - - return if (cheated) { - paths - } else { - position.neighbors() - .filter { it in this && this[it] == '#' } + val part1 = shortest.flatMap { (step, fromStart) -> + step.neighbors() + .filter { it in maze && maze[it] == '#' } .flatMap { it.neighbors() } - .filter { canVisit(it) } - .fold(paths) { list, next -> - list.addAll(findPaths(next, goal, newVisited, time + 2, true)) - list - } - } + .filter { it != step && it in maze && maze[it] != '#' } + .mapNotNull { toGoal[it] } + .map { fromStart + 2 + it } + }.count { it <= total - 100 } + println("Part 1: $part1") } + +fun CharGrid.findShortestPath(start: Grid.Coordinate, end: Grid.Coordinate) = + dijkstraPath(start, goal = { it == end }, neighbors = { position -> + position.neighbors() + .filter { it in this && this[it] != '#' } + .map { it to 1 } + })?.steps