Day 18.
This commit is contained in:
parent
b0e4461a7d
commit
1b2332a49e
@ -75,11 +75,11 @@ class CharGrid {
|
|||||||
val data: Array<CharArray>
|
val data: Array<CharArray>
|
||||||
val bChar: Char
|
val bChar: Char
|
||||||
|
|
||||||
constructor(width: Int, height: Int, borderChar: Char = ' ') {
|
constructor(width: Int, height: Int, borderChar: Char = ' ', fillChar: Char = borderChar) {
|
||||||
bChar = borderChar
|
bChar = borderChar
|
||||||
this.width = width
|
this.width = width
|
||||||
this.height = height
|
this.height = height
|
||||||
data = Array(height) { CharArray(width) { bChar } }
|
data = Array(height) { CharArray(width) { fillChar } }
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(input: List<String>, borderChar: Char = ' ') {
|
constructor(input: List<String>, borderChar: Char = ' ') {
|
||||||
|
127
src/aoc2024/Day18.kt
Normal file
127
src/aoc2024/Day18.kt
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 18: RAM Run ---
|
||||||
|
https://adventofcode.com/2024/day/18
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
5,4
|
||||||
|
4,2
|
||||||
|
4,5
|
||||||
|
3,0
|
||||||
|
2,1
|
||||||
|
6,3
|
||||||
|
2,4
|
||||||
|
1,5
|
||||||
|
0,6
|
||||||
|
3,3
|
||||||
|
2,6
|
||||||
|
5,1
|
||||||
|
1,2
|
||||||
|
5,5
|
||||||
|
2,5
|
||||||
|
6,5
|
||||||
|
1,4
|
||||||
|
0,4
|
||||||
|
6,4
|
||||||
|
1,1
|
||||||
|
6,1
|
||||||
|
1,0
|
||||||
|
0,5
|
||||||
|
1,6
|
||||||
|
2,0
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val pos: RelPos, val relLen: Int)
|
||||||
|
|
||||||
|
fun part1(input: List<String>, steps: Int, wh: Int): Int {
|
||||||
|
val posList = input.map { it.splitInts(",").toIntArray() }
|
||||||
|
val grid = CharGrid(wh, wh, '#', fillChar = '.')
|
||||||
|
posList.take(steps).forEach { grid[it[0], it[1]] = '#' }
|
||||||
|
val queue = PriorityQueue<Node>(Comparator.comparing { -it.pos.dc - it.pos.dr })
|
||||||
|
queue.add(Node(RelPos(0, 0), 0))
|
||||||
|
val bestCosts = HashMap<RelPos, Int>()
|
||||||
|
var minCost = Int.MAX_VALUE
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.remove()
|
||||||
|
val pos = node.pos
|
||||||
|
val cost = node.relLen
|
||||||
|
if (pos.dc == wh - 1 && pos.dr == wh - 1) {
|
||||||
|
minCost = minCost.coerceAtMost(cost)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos] ?: Int.MAX_VALUE) < cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos] = cost
|
||||||
|
queue.addAll(CharGrid.PLUS_POS
|
||||||
|
.map { pos.translate(it) }
|
||||||
|
.filter { grid[it] == '.' && (bestCosts[it] ?: Int.MAX_VALUE) > cost + 1 }
|
||||||
|
.map { Node(it, cost + 1) })
|
||||||
|
}
|
||||||
|
return minCost
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>, wh: Int): RelPos {
|
||||||
|
val posList = input.map { it.splitInts(",").toIntArray() }
|
||||||
|
var lowerBound = 0
|
||||||
|
var higherBound = posList.size
|
||||||
|
do {
|
||||||
|
val grid = CharGrid(wh, wh, '#', fillChar = '.')
|
||||||
|
val boulderPos = (lowerBound + higherBound) / 2
|
||||||
|
posList.take(boulderPos).forEach { grid[it[0], it[1]] = '#' }
|
||||||
|
|
||||||
|
var foundPath = false
|
||||||
|
val queue = PriorityQueue<Node>(Comparator.comparing { -it.pos.dc - it.pos.dr })
|
||||||
|
queue.add(Node(RelPos(0, 0), 0))
|
||||||
|
val bestCosts = HashMap<RelPos, Int>()
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.remove()
|
||||||
|
val pos = node.pos
|
||||||
|
val cost = node.relLen
|
||||||
|
if (pos.dc == wh - 1 && pos.dr == wh - 1) {
|
||||||
|
foundPath = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos] ?: Int.MAX_VALUE) < cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos] = cost
|
||||||
|
queue.addAll(CharGrid.PLUS_POS
|
||||||
|
.map { pos.translate(it) }
|
||||||
|
.filter { grid[it] == '.' && (bestCosts[it] ?: Int.MAX_VALUE) > cost + 1 }
|
||||||
|
.map { Node(it, cost + 1) })
|
||||||
|
}
|
||||||
|
if (foundPath) {
|
||||||
|
lowerBound = boulderPos + 1
|
||||||
|
} else {
|
||||||
|
higherBound = boulderPos
|
||||||
|
}
|
||||||
|
} while (lowerBound < higherBound)
|
||||||
|
println("$lowerBound $higherBound")
|
||||||
|
return RelPos(posList[lowerBound - 1][0], posList[lowerBound - 1][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day18_test")
|
||||||
|
val testInputPart1Result = part1(testInput, 12, 7)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput, 7)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 22)
|
||||||
|
check(testInputPart2Result == RelPos(6, 1))
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day18")
|
||||||
|
part1(input, 1024, 71).println()
|
||||||
|
part2(input, 71).println()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user