From aa0f1dad746040c103415f8d55183aa181372e7b Mon Sep 17 00:00:00 2001 From: chrisly42 Date: Fri, 20 Dec 2024 07:19:34 +0100 Subject: [PATCH] Day 20. --- src/aoc2024/Day20.kt | 104 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/aoc2024/Day20.kt diff --git a/src/aoc2024/Day20.kt b/src/aoc2024/Day20.kt new file mode 100644 index 0000000..c167cc4 --- /dev/null +++ b/src/aoc2024/Day20.kt @@ -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 { + val queue = ArrayDeque() + queue.add(Node(startPos, 0)) + val bestCosts = HashMap() + 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): 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): 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() +}