Day 20.
This commit is contained in:
parent
9047a97185
commit
aa0f1dad74
104
src/aoc2024/Day20.kt
Normal file
104
src/aoc2024/Day20.kt
Normal file
@ -0,0 +1,104 @@
|
||||
package aoc2024
|
||||
|
||||
import CharGrid
|
||||
import RelPos
|
||||
import println
|
||||
import readInput
|
||||
import kotlin.math.abs
|
||||
|
||||
/*
|
||||
--- Day 20: Race Condition ---
|
||||
https://adventofcode.com/2024/day/20
|
||||
*/
|
||||
fun main() {
|
||||
|
||||
val inlineTestInput = """
|
||||
###############
|
||||
#...#...#.....#
|
||||
#.#.#.#.#.###.#
|
||||
#S#...#.#.#...#
|
||||
#######.#.#.###
|
||||
#######.#.#...#
|
||||
#######.#.###.#
|
||||
###..E#...#...#
|
||||
###.#######.###
|
||||
#...###...#...#
|
||||
#.#####.#.###.#
|
||||
#.#...#.#.#...#
|
||||
#.#.#.#.#.#.###
|
||||
#...#...#...###
|
||||
###############
|
||||
"""
|
||||
|
||||
data class Node(val pos: RelPos, val relLen: Int)
|
||||
|
||||
fun getPathCosts(grid: CharGrid, startPos: RelPos, endPos: RelPos): HashMap<RelPos, Int> {
|
||||
val queue = ArrayDeque<Node>()
|
||||
queue.add(Node(startPos, 0))
|
||||
val bestCosts = HashMap<RelPos, Int>()
|
||||
while (queue.isNotEmpty()) {
|
||||
val node = queue.removeFirst()
|
||||
val pos = node.pos
|
||||
val cost = node.relLen
|
||||
bestCosts[pos] = cost
|
||||
if (pos == endPos) {
|
||||
break
|
||||
}
|
||||
queue.addAll(CharGrid.PLUS_POS
|
||||
.map { pos.translate(it) }
|
||||
.filter { grid[it] == '.' && !bestCosts.contains(it) }
|
||||
.map { Node(it, cost + 1) })
|
||||
}
|
||||
return bestCosts
|
||||
}
|
||||
|
||||
fun part1(input: List<String>): Int {
|
||||
val grid = CharGrid(input)
|
||||
val start = grid.findMatchesRelPos { it == 'S' }[0]
|
||||
val end = grid.findMatchesRelPos { it == 'E' }[0]
|
||||
grid[end] = '.'
|
||||
|
||||
val cheats = listOf(
|
||||
RelPos(0, -1), RelPos(-1, 0), RelPos(1, 0), RelPos(0, 1),
|
||||
RelPos(0, -2), RelPos(-2, 0), RelPos(2, 0), RelPos(0, 2),
|
||||
)
|
||||
val pathCosts = getPathCosts(grid, start, end)
|
||||
|
||||
return pathCosts.asSequence().sumOf { (pos, cost) ->
|
||||
cheats.map { pos.translate(it) to (abs(it.dr) + abs(it.dc)) }
|
||||
.filter { (pathCosts[it.first] ?: 0) - (cost + it.second) >= 100 }
|
||||
.map { it.first to (pathCosts[it.first]!! - (cost + it.second)) }.count()
|
||||
}
|
||||
}
|
||||
|
||||
fun part2(input: List<String>): Int {
|
||||
val grid = CharGrid(input)
|
||||
val start = grid.findMatchesRelPos { it == 'S' }[0]
|
||||
val end = grid.findMatchesRelPos { it == 'E' }[0]
|
||||
grid[end] = '.'
|
||||
|
||||
val pathCosts = getPathCosts(grid, start, end)
|
||||
|
||||
// manhattan distance!
|
||||
val pathCostsList = pathCosts.toList()
|
||||
return pathCostsList.sumOf { (pos, cost) ->
|
||||
pathCostsList.asSequence().filter { abs(pos.dc - it.first.dc) + abs(pos.dr - it.first.dr) <= 20 }
|
||||
.filter { (pathCosts[it.first] ?: 0) - (cost + abs(pos.dc - it.first.dc) + abs(pos.dr - it.first.dr)) >= 100 }
|
||||
.map { it.first to (pathCosts[it.first]!! - (cost + it.second)) }.count()
|
||||
}
|
||||
}
|
||||
|
||||
// test if implementation meets criteria from the description, like:
|
||||
val testInput = inlineTestInput.trim().reader().readLines()
|
||||
//val testInput = readInput("aoc2024/Day20_test")
|
||||
val testInputPart1Result = part1(testInput)
|
||||
println("Part 1 Test: $testInputPart1Result")
|
||||
val testInputPart2Result = part2(testInput)
|
||||
println("Part 2 Test: $testInputPart2Result")
|
||||
check(testInputPart1Result == 0)
|
||||
check(testInputPart2Result == 0)
|
||||
|
||||
val input = readInput("aoc2024/Day20")
|
||||
part1(input).println()
|
||||
part2(input).println()
|
||||
}
|
Loading…
Reference in New Issue
Block a user