generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Y2023D13.kt
89 lines (77 loc) · 3.63 KB
/
Y2023D13.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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package aockt.y2023
import aockt.util.parse
import io.github.jadarma.aockt.core.Solution
object Y2023D13 : Solution {
/**
* Notes on the terrain of Lava Island, but without any lava...
* @param data The terrain reading, first element is first row, then going upwards. Should only contain `[#.]`.
*/
private class LavaIslandMap(data: List<String>) {
// The terrain readings, transposed to make symmetry scanning, well... symmetrical!
private val byRow: List<BooleanArray>
private val byColumn: List<BooleanArray>
init {
require(data.isNotEmpty()) { "No data to process." }
val height = data.size
val width = data.first().length
require(data.all { it.length == width }) { "Map is not perfect grid." }
byRow = List(height) { BooleanArray(width) }
byColumn = List(width) { BooleanArray(height) }
for (y in 0..<height) {
for (x in 0..<width) {
val state = when (data[y][x]) {
'.' -> false
'#' -> true
else -> error("Invalid map state.")
}
byRow[height - 1 - y][x] = state
byColumn[x][height - 1 - y] = state
}
}
}
/** Returns the total amount of differences between the contents, 0 meaning perfect match. */
private fun BooleanArray.compareTo(other: BooleanArray): Int {
require(size == other.size) { "Cannot compare collections of different sizes." }
return indices.count { index -> this[index] != other[index] }
}
/**
* Find symmetries in the [pattern], allowing for exactly this amount of [smudges].
* For each symmetry line found, returns the number of lines below it.
*/
private fun findSymmetries(pattern: List<BooleanArray>, smudges: Int): List<Int> = buildList {
for (mirror in 1..<pattern.size) {
var errors = 0
for (reflectionLine in 1..minOf(mirror, pattern.size - mirror)) {
val above = pattern[mirror + reflectionLine - 1]
val below = pattern[mirror - reflectionLine]
errors += above.compareTo(below)
if (errors > smudges) break
}
if (errors == smudges) add(mirror)
}
}
/**
* Summarize your notes on the observed volcanic pattern.
* @param smudges How many smudges to expect there to be, exactly.
* @return The sum between the total amount of columns to the left of each vertical reflection and 100 times
* the total amount of rows above each horizontal reflection.
*/
fun summarize(smudges: Int): Int {
val horizontal = findSymmetries(byRow, smudges)
val vertical = findSymmetries(byColumn, smudges)
return horizontal.sum() * 100 + vertical.sum()
}
}
/** Parse the [input] and return the list of [LavaIslandMap]s. */
private fun parseInput(input: String): List<LavaIslandMap> = parse {
val inputRegex = Regex("""^[.#]+$""")
input
.splitToSequence("\n\n")
.map { map -> map.lines().reversed() }
.onEach { points -> points.all { it.matches(inputRegex) } }
.map(::LavaIslandMap)
.toList()
}
override fun partOne(input: String) = parseInput(input).sumOf { it.summarize(smudges = 0) }
override fun partTwo(input: String) = parseInput(input).sumOf { it.summarize(smudges = 1) }
}