Skip to content

Commit

Permalink
2024 - Day 18 - part 1&2 - part 2 brute forced (1500ms) - I'm sure th…
Browse files Browse the repository at this point in the history
…ere is some other way.
  • Loading branch information
fmmr committed Dec 18, 2024
1 parent 521d09c commit 4d03fb7
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 0 deletions.
70 changes: 70 additions & 0 deletions src/main/kotlin/no/rodland/advent_2024/Day18.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package no.rodland.advent_2024

import no.rodland.advent.Day
import no.rodland.advent.Pos

// template generated: 18/12/2024
// Fredrik Rødland 2024

class Day18(val input: List<String>, private val bytesToTake: Int, maxIndex: Int) : Day<Int, Pos, List<Pos>> {

private val parsed = input.parse()
private val start = Pos(0, 0)
private val end = Pos(maxIndex, maxIndex)

override fun partOne(): Int {
return bfs(bytesToTake).size - 1
}

override fun partTwo(): Pos {
val idx = (bytesToTake..parsed.size).first { n ->
bfs(n).isEmpty()
}
return parsed[idx - 1]
}

private fun bfs(n: Int, set: Set<Pos> = parsed.take(n).toSet()) = bfs(start, end, { p ->
p.neighbourCellsUDLR()
.filter { it !in set }
.filter { it.x >= start.x }
.filter { it.y >= start.y }
.filter { it.x <= end.x }
.filter { it.y <= end.y }
})

private fun bfs(
start: Pos,
end: Pos,
nextFunction: (Pos) -> List<Pos>,
queue: ArrayDeque<List<Pos>> = ArrayDeque(listOf(listOf(start))),
visited: MutableSet<Pos> = mutableSetOf(start)
): List<Pos> {
if (queue.isEmpty()) return emptyList()
// Dequeue the current path
val path = queue.removeFirst()
val current = path.last()

// Check if we reached the end position
if (current == end) return path

// Explore neighbors
for (neighbor in nextFunction(current)) {
if (!visited.contains(neighbor)) {
visited.add(neighbor)
queue.add(path + neighbor) // Enqueue the new path
}
}

// Recursive call with the updated queue and visited set
return bfs(start, end, nextFunction, queue, visited)
}

override fun List<String>.parse(): List<Pos> {
return map { line ->
val (first, second) = line.split(",").map { it.toInt() }
Pos(first, second)
}
}

override val day = "18".toInt()
}
85 changes: 85 additions & 0 deletions src/test/kotlin/no/rodland/advent_2024/Day18Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package no.rodland.advent_2024

import no.rodland.advent.*
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import readFile

//
// run: download_aoc_input.sh to download input
//

@Suppress("ClassName")
@DisableSlow
internal class Day18Test {
private val data18 = "2024/input_18.txt".readFile()
private val test18 = "2024/input_18_test.txt".readFile()

private val resultTestOne = 22
private val resultTestTwo = Pos(6,1)
private val resultOne = 286
private val resultTwo = Pos(20,64)

val test = defaultTestSuiteParseOnInit(
Day18(data18, 1024, 70),
Day18(test18, 12, 6),
resultTestOne,
resultOne,
resultTestTwo,
resultTwo,
{ Day18(data18, 1024, 70) },
{ Day18(test18, 12, 6) },
numTestPart1 = 20,
numTestPart2 = 1
)

@Nested
inner class Init {
@Test
fun `18,-,example,1`() {
report(AOCTest({ "123".toInt() }, Unit, 123, 5, "18".toInt(), Part.TWO, false, "example"))
}

@Test
fun `18,-,example,2`() {
report(test.initTest.copy())
}

@Test
fun `18,-,test,init`() {
report(test.initTest)
}

@Test
fun `18,-,live,init`() {
report(test.initLive)
}
}

@Nested
inner class `Part 1` {
@Test
fun `18,1,test`() {
report(test.testPart1)
}

@Test
fun `18,1,live,1`() {
report(test.livePart1)
}
}

@Nested
inner class `Part 2` {
@Test
fun `18,2,test`() {
report(test.testPart2)
}

@Test
@Slow(1500)
fun `18,2,live,1`() {
report(test.livePart2)
}
}
}
25 changes: 25 additions & 0 deletions src/test/resources/2024/input_18_test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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

0 comments on commit 4d03fb7

Please sign in to comment.