generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2022D15.kt
68 lines (55 loc) · 2.8 KB
/
Y2022D15.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package aockt.y2022
import io.github.jadarma.aockt.core.Solution
import kotlin.math.absoluteValue
object Y2022D15 : Solution {
/** Represents a discrete point in 2D space. */
private data class Point(val x: Int, val y: Int)
/** Returns the Manhattan distance between the two points. */
private infix fun Point.manhattanDistanceTo(other: Point): Int =
(x - other.x).absoluteValue + (y - other.y).absoluteValue
/** Generates all points that are [radius] units away (using Manhattan distance) from the original point. */
private fun Point.manhattanCircle(radius: Int): Sequence<Point> = sequence {
var cx = x - radius
var cy = y
repeat(radius) { yield(Point(cx, cy)); cx++; cy++ }
repeat(radius) { yield(Point(cx, cy)); cx++; cy-- }
repeat(radius) { yield(Point(cx, cy)); cx--; cy-- }
repeat(radius) { yield(Point(cx, cy)); cx--; cy++ }
}
/** A distress beacon sensor and its current detection [range]. */
private data class Sensor(val location: Point, val range: Int)
/** Tests if the [point] is inside the sensor's detection range. */
private fun Sensor.observes(point: Point): Boolean = location.manhattanDistanceTo(point) <= range
/** Expression that validates a sensor reading. */
private val inputRegex = Regex("""^Sensor at x=(-?\d+), y=(-?\d+): closest beacon is at x=(-?\d+), y=(-?\d+)$""")
/** Parse the [input] and return the sequence of sensor readings as pairs of sensor to beacon locations. */
private fun parseInput(input: String): Map<Sensor, Point> =
input
.lineSequence()
.map { line -> inputRegex.matchEntire(line)!!.groupValues.drop(1).map(String::toInt) }
.associate { (sx, sy, bx, by) ->
val sensor = Point(sx, sy)
val beacon = Point(bx, by)
Sensor(sensor, sensor.manhattanDistanceTo(beacon)) to beacon
}
override fun partOne(input: String): Int {
val scan = parseInput(input)
val range = scan.keys.minOf { it.location.x - it.range }.rangeTo(scan.keys.maxOf { it.location.x + it.range })
val targetLine = 2_000_000
val beaconsOnLine = scan.values.filter { it.y == targetLine }.toSet()
return range.count { x ->
val p = Point(x, targetLine)
p !in beaconsOnLine && scan.keys.any { it.observes(p) }
}
}
override fun partTwo(input: String): Long {
val scan = parseInput(input)
val scanRange = 0..4_000_000
return scan.keys
.asSequence()
.flatMap { (location, range) -> location.manhattanCircle(range + 1) }
.filter { it.x in scanRange && it.y in scanRange }
.first { p -> scan.keys.none { it.observes(p) } }
.let { it.x * 4_000_000L + it.y }
}
}