generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2023D03.kt
66 lines (55 loc) · 2.4 KB
/
Y2023D03.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
package aockt.y2023
import aockt.util.spacial.Point
import aockt.util.spacial.Area
import aockt.util.spacial.coerceIn
import aockt.util.parse
import io.github.jadarma.aockt.core.Solution
object Y2023D03 : Solution {
/** A part number label in the engine schematics, drawn at [coords] with a numeric [value]. */
private data class PartNumber(val coords: Area, val value: Int)
/** A symbol in the engine schematics, drawn at [coords] with the symbol [value]. */
private data class Symbol(val coords: Point, val value: Char)
/** Parse the [input] and build a representation of the engine schematics, mapping each symbol to adjacent parts. */
private fun parseInput(input: String): Map<Symbol, List<PartNumber>> = parse {
val lines: Array<String> = input.lines().toTypedArray().also {
require(it.isNotEmpty())
require(it.all { line -> line.length == it.first().length })
}
val bounds = Area(0..<lines.first().length, lines.indices)
fun adjacentSymbolOrNull(part: PartNumber): Symbol? {
val searchSpace = with(part.coords) {
Area(
xRange = xRange.run { first - 1..last + 1 },
yRange = yRange.run { first - 1..last + 1 },
)
}.coerceIn(bounds)
for (point in searchSpace) {
val value = lines[point.y.toInt()][point.x.toInt()]
if (value != '.' && value.isLetterOrDigit().not()) {
return Symbol(point, value)
}
}
return null
}
buildMap<Symbol, MutableList<PartNumber>> {
val numberRegex = Regex("""[1-9]\d*""")
lines.forEachIndexed { y, row ->
numberRegex.replace(row) { label ->
val part = PartNumber(Area(label.range, y..y), label.value.toInt())
adjacentSymbolOrNull(part)?.let { symbol -> getOrPut(symbol) { mutableListOf() }.add(part) }
""
}
}
}
}
override fun partOne(input: String) =
parseInput(input)
.values
.sumOf { parts -> parts.sumOf { it.value } }
override fun partTwo(input: String) =
parseInput(input)
.filterKeys { it.value == '*' }
.filterValues { it.size == 2 }
.values
.sumOf { (l, r) -> l.value * r.value }
}