Skip to content

Commit

Permalink
day 21 was a tough one
Browse files Browse the repository at this point in the history
  • Loading branch information
norganos committed Dec 21, 2023
1 parent 5bf7f7a commit fc7b1e4
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 8 deletions.
184 changes: 181 additions & 3 deletions src/main/kotlin/de/linkel/aoc/Day21.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,191 @@ package de.linkel.aoc

import de.linkel.aoc.base.AbstractLinesAdventDay
import de.linkel.aoc.base.QuizPart
import de.linkel.aoc.utils.grid.Area
import de.linkel.aoc.utils.grid.Point
import jakarta.inject.Singleton
import java.util.*

@Singleton
class Day21: AbstractLinesAdventDay<Int>() {
class Day21: AbstractLinesAdventDay<Long>() {
override val day = 21

override fun process(part: QuizPart, lines: Sequence<String>): Int {
return 0
override fun process(part: QuizPart, lines: Sequence<String>): Long {
var width = 0
var height = 0
var start = Point(0,0)
val rocks = lines
.flatMapIndexed { y, line ->
if (width == 0) width = line.length
if (height <= y) height = y + 1
line
.mapIndexed { x, c ->
if (c == 'S')
start = Point(x, y)
if (c == '#')
Point(x, y)
else null
}
.filterNotNull()
}
.toSet()

val area = Area(0, 0, width, height)
return if (part == QuizPart.A)
dijkstra1(area, rocks, start, if (width < 15) 6 else 64).size.toLong()
else {
assert(width == height)
assert(width % 2 == 1)
assert(height % 2 == 1)
val max = if (width < 15) 5000 else 26501365
val step = width * 2
val rem = max % step

if (width < 15) {
println(" 6: ${dijkstra2(area, rocks, start, 6)}")
println(" 10: ${dijkstra2(area, rocks, start, 10)}")
println(" 50: ${dijkstra2(area, rocks, start, 50)}")
println("100: ${dijkstra2(area, rocks, start, 100)}")
println("500: ${dijkstra2(area, rocks, start, 500)}")
}

val probe = 5
(0..probe)
.map { i -> dijkstra2(area, rocks, start, step * i + rem) }
.toSeq()
.prepare()
.toExtrapolation()
.extrapolate((max - rem) / step - probe)
}
}

fun dijkstra1(area: Area, rocks: Set<Point>, start: Point, max: Int): Set<Point> {
val queue = PriorityQueue<Pair<Point, Int>>(compareBy { it.second })
queue.add(start to 0)
val seen = mutableSetOf(start)
val visited = mutableSetOf<Point>()
while (queue.isNotEmpty()) {
val (point, distance) = queue.remove()
if (distance > max) {
break
}
if (distance % 2 == max % 2) {
visited.add(point)
}

listOf(
point + NORTH,
point + WEST,
point + SOUTH,
point + EAST
)
.filter { it in area }
.filter { it !in rocks }
.filter { it !in seen }
.forEach {
queue.add(it to distance + 1)
seen.add(it)
}
}
return visited
}

fun dijkstra2(area: Area, rocks: Set<Point>, start: Point, max: Int): Long {
val queue = PriorityQueue<Pair<Point, Int>>(compareBy { it.second })
queue.add(start to 0)
val seen = mutableSetOf(start)
var count = 0L
while (queue.isNotEmpty()) {
val (point, distance) = queue.remove()
if (distance > max) {
break
}
if (distance % 2 == max % 2) {
count++
}

listOf(
point + NORTH,
point + WEST,
point + SOUTH,
point + EAST
)
.filter {
if (it in area)
it !in rocks
else {
val p = Point(it.x pmod area.width, it.y pmod area.height)
p in area && p !in rocks
}
}
.filter { it !in seen }
.forEach {
queue.add(it to distance + 1)
seen.add(it)
}
}
return count
}

private infix fun Int.pmod(other: Int): Int {
val result = this % other
return if (result < 0)
result + other
else result
}

private fun List<Long>.toSeq(): Seq = Seq(this)

class Seq(
input: List<Long>,
val parent: Seq? = null
) {
var values = input.toMutableList()
private set

fun prepare(): Seq {
return if (values.last() == 0L) this
else differentiate().prepare()
}

fun differentiate(): Seq {
val diffs = values
.windowed(2)
.map { (a, b) -> b - a }
.toList()
if (diffs.isEmpty())
throw Exception("not enough input iterations")
return Seq(
input = diffs,
parent = this
)
}

private fun lasts(): List<Long>
= listOf(values.last()) + (parent?.lasts() ?: emptyList())

fun toExtrapolation(): Extrapolation {
return Extrapolation(lasts())
}
}

class Extrapolation(
val inputs: List<Long>
) {
init {
assert(inputs.first() == 0L)
}

fun extrapolate(steps: Int): Long {
val vals = inputs.toMutableList()
repeat(steps) {
vals.indices.forEach { i ->
if (i < vals.lastIndex) {
vals[i+1] += vals[i]
}
}
}
return vals.last()
}
}
}
23 changes: 18 additions & 5 deletions src/test/kotlin/de/linkel/aoc/Day21Test.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
package de.linkel.aoc

class Day21Test: AbstractDayTest<Int>() {
class Day21Test: AbstractDayTest<Long>() {
override val exampleA = """
...........
.....###.#.
.###.##..#.
..#.#...#..
....#.#....
.##..S####.
.##..#...#.
.......##..
.##.#.####.
.##..##.##.
...........
""".trimIndent()
override val exampleSolutionA = 0
override val solutionA = 0
override val exampleSolutionA = 16L
override val solutionA = 3773L

override val exampleSolutionB = 0
override val solutionB = 0
override val exampleSolutionB = 16733044L
override val solutionB = 625628021226274L

override val implementation = Day21()
}
// 6872
// 3774

0 comments on commit fc7b1e4

Please sign in to comment.