Skip to content

Commit 742fb73

Browse files
committed
Day 15 - puzzle 2.
1 parent 978d41c commit 742fb73

File tree

1 file changed

+59
-26
lines changed

1 file changed

+59
-26
lines changed

src/day_15/puzzle.kt

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,84 @@ package day_15
33
import println
44
import readInput
55
import kotlin.math.absoluteValue
6+
import kotlin.math.max
67

7-
data class Point(val x: Int, val y: Int) {
8-
override fun toString() = "($x; $y)"
8+
data class Point(val x: Int, val y: Int)
9+
10+
data class Signal(val sensor: Point, val beacon: Point) {
11+
val distance by lazy { sensor - beacon }
912
}
1013

1114
private operator fun Point.minus(other: Point) = (x - other.x).absoluteValue + (y - other.y).absoluteValue
1215

13-
private fun MatchResult.toPoint() = Point(
14-
groupValues[1].toInt(),
15-
groupValues[2].toInt(),
16-
)
16+
private infix fun Pair<Int, Int>.merge(other: Pair<Int, Int>) = first to max(second, other.second)
1717

18-
fun puzzle1(input: List<String>, row: Int): Int {
19-
val ignoreX = hashSetOf<Int>()
20-
val xRanges = mutableListOf<IntRange>()
18+
class Grid(private val pairs: List<Signal>) {
19+
fun takenPositions(row: Int) = takenRanges(row)
20+
.sortedBy { it.first }
21+
.reduce { acc, next -> acc merge next }
22+
.let { (a, b) -> b - a }
2123

22-
input.forEach { line ->
23-
val (match1, match2) = """x=(-?\d+), y=(-?\d+)""".toRegex().findAll(line).toList()
24+
fun tuningFrequency(limit: Int): Long {
25+
for (row in 0..limit) {
26+
val ranges = takenRanges(row)
27+
.sortedBy { it.first }
28+
.map { (a, b) -> a.coerceIn(0, limit) to b.coerceIn(0, limit) }
2429

25-
val sensor = match1.toPoint()
26-
val beacon = match2.toPoint()
27-
val distance = sensor - beacon
28-
val radius = if (sensor.y < row) { sensor.y + distance - row } else { row - (sensor.y - distance) }
30+
var fullRange = ranges.first()
31+
for (i in 1 until ranges.size) {
32+
val col = fullRange.second + 1
2933

30-
if (radius > 0) {
31-
xRanges += (sensor.x - radius)..(sensor.x + radius)
34+
// Lookup for gap
35+
if (col < ranges[i].first) {
36+
return 4_000_000L * col + row
37+
}
38+
39+
fullRange = fullRange merge ranges[i]
40+
}
3241
}
3342

34-
if (beacon.y == row) {
35-
ignoreX += beacon.x
43+
return -1
44+
}
45+
46+
private fun takenRanges(row: Int) = buildList {
47+
pairs.forEach { signal ->
48+
val radius = signal.rowRadius(row)
49+
if (radius > 0) {
50+
this += (signal.sensor.x - radius) to (signal.sensor.x + radius)
51+
}
3652
}
3753
}
3854

39-
return xRanges
40-
.zipWithNext { a, b -> a.toSet() union b.toSet() }
41-
.flatten()
42-
.distinct()
43-
.subtract(ignoreX)
44-
.size
55+
private fun Signal.rowRadius(row: Int) = if (sensor.y < row) {
56+
sensor.y + distance - row
57+
} else {
58+
row - (sensor.y - distance)
59+
}
60+
}
61+
62+
private fun MatchResult.toPoint() = Point(
63+
groupValues[1].toInt(),
64+
groupValues[2].toInt(),
65+
)
66+
67+
private fun List<String>.toPairs() = map {
68+
"""x=(-?\d+), y=(-?\d+)""".toRegex()
69+
.findAll(it)
70+
.toList()
71+
.let { (match1, match2) -> Signal(match1.toPoint(), match2.toPoint()) }
4572
}
4673

74+
fun puzzle1(input: List<String>, row: Int) = Grid(input.toPairs()).takenPositions(row)
75+
76+
fun puzzle2(input: List<String>, limit: Int) = Grid(input.toPairs()).tuningFrequency(limit)
77+
4778
fun main() {
4879
val testInput = readInput("day_15/input_test")
4980
check(puzzle1(testInput, 10) == 26)
81+
check(puzzle2(testInput, 20) == 56_000_011L)
5082

5183
val input = readInput("day_15/input")
52-
puzzle1(input, 2_000_000).println() // 4873353
84+
puzzle1(input, 2_000_000).println()
85+
puzzle2(input, 4_000_000).println()
5386
}

0 commit comments

Comments
 (0)