Skip to content

Commit ff24655

Browse files
committed
Day 18: Union-Find
1 parent 08d78e4 commit ff24655

File tree

1 file changed

+53
-20
lines changed
  • kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024

1 file changed

+53
-20
lines changed

kt/aoc2024-lib/src/commonMain/kotlin/com/github/ephemient/aoc2024/Day18.kt

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,66 @@ class Day18(input: String, private val size: Int = 70) {
77
x to y
88
}.toList()
99

10-
private fun findPath(obstacles: Iterable<IntPair>): Set<IntPair>? {
11-
val visited = obstacles.toMutableSet()
12-
val queue = ArrayDeque<Node>()
13-
queue.add(Node(0, 0))
10+
fun part1(limit: Int = 1024): Int? {
11+
val visited = coords.subList(0, limit).toMutableSet().apply { add(0 to 0) }
12+
val queue = ArrayDeque<IndexedValue<IntPair>>()
13+
queue.add(IndexedValue(0, 0 to 0))
1414
while (queue.isNotEmpty()) {
15-
val node = queue.removeFirst()
16-
val (x, y) = node
17-
if (x == size && y == size) return generateSequence(node) { it.next }.map { it.x to it.y }.toSet()
18-
if (!visited.add(x to y)) continue
19-
if (x > 0) queue.addLast(Node(x - 1, y, node))
20-
if (y > 0) queue.addLast(Node(x, y - 1, node))
21-
if (y < size) queue.addLast(Node(x, y + 1, node))
22-
if (x < size) queue.addLast(Node(x + 1, y, node))
15+
val (t, pos) = queue.removeFirst()
16+
val (x, y) = pos
17+
if (x == size && y == size) return t
18+
if (x > 0) (x - 1 to y).let { if (visited.add(it)) queue.addLast(IndexedValue(t + 1, it)) }
19+
if (y > 0) (x to y - 1).let { if (visited.add(it)) queue.addLast(IndexedValue(t + 1, it)) }
20+
if (y < size) (x to y + 1).let { if (visited.add(it)) queue.addLast(IndexedValue(t + 1, it)) }
21+
if (x < size) (x + 1 to y).let { if (visited.add(it)) queue.addLast(IndexedValue(t + 1, it)) }
2322
}
2423
return null
2524
}
2625

27-
fun part1(limit: Int = 1024): Int? = findPath(coords.subList(0, limit))?.size?.minus(1)
28-
2926
fun part2(): String? {
30-
var i = 0
31-
while (i < coords.size) {
32-
val path = findPath(coords.subList(0, i + 1)) ?: return coords[i].let { (x, y) -> "$x,$y" }
33-
do i++ while (i < coords.size && coords[i] !in path)
27+
val coords = coords.toMutableSet()
28+
val sets = UnionFind<IntPair>()
29+
for (x in 0..size) {
30+
for (y in 0..size) {
31+
val pos = x to y
32+
if (pos in coords) continue
33+
sets[pos]
34+
if (x < size) (x + 1 to y).takeIf { it !in coords }?.let { sets[pos] = it }
35+
if (y < size) (x to y + 1).takeIf { it !in coords }?.let { sets[pos] = it }
36+
}
3437
}
35-
return null
38+
val src = 0 to 0
39+
val dst = size to size
40+
val (x, y) = coords.toList().asReversed().firstOrNull { pos ->
41+
coords.remove(pos)
42+
sets[pos]
43+
val (x, y) = pos
44+
if (x > 0) (x - 1 to y).takeIf { it !in coords }?.let { sets[pos] = it }
45+
if (y > 0) (x to y - 1).takeIf { it !in coords }?.let { sets[pos] = it }
46+
if (y < size) (x to y + 1).takeIf { it !in coords }?.let { sets[pos] = it }
47+
if (x < size) (x + 1 to y).takeIf { it !in coords }?.let { sets[pos] = it }
48+
sets[src] == sets[dst]
49+
} ?: return null
50+
return "$x,$y"
3651
}
3752

38-
private data class Node(val x: Int, val y: Int, val next: Node? = null)
53+
private class UnionFind<T> {
54+
private val sets = mutableMapOf<T, T>()
55+
56+
operator fun get(key: T): T {
57+
var key = key
58+
var value = sets.getOrPut(key) { key }
59+
while (key != value) {
60+
val next = sets.getOrPut(value) { value }
61+
sets[key] = next
62+
key = value
63+
value = next
64+
}
65+
return value
66+
}
67+
68+
operator fun set(key: T, value: T) {
69+
sets[get(key)] = get(value)
70+
}
71+
}
3972
}

0 commit comments

Comments
 (0)