@@ -3,51 +3,84 @@ package day_15
33import println
44import readInput
55import 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
1114private 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+
4778fun 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