generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Y2021D20.kt
93 lines (81 loc) · 3.88 KB
/
Y2021D20.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
90
91
92
93
package aockt.y2021
import io.github.jadarma.aockt.core.Solution
object Y2021D20 : Solution {
/**
* Represents a finite slice of an infinite image, with a maximum size of 2^15 * 2^15 pixels.
* @property width The finite width of this slice.
* @property height The finite height of this slice.
* @property defaultValue The value of pixels outside the bounds of this slice.
*/
private class InfiniteImageSlice(
val width: Int,
val height: Int,
val defaultValue: Boolean = false,
private val imageData: BooleanArray,
) {
init {
require(width > 0 && height > 0) { "Image dimensions need to be positive." }
require(width <= Short.MAX_VALUE && height <= Short.MAX_VALUE) { "Image maximum size exceeded." }
require(imageData.size == width * height) { "Image dimensions and data size do not match." }
}
operator fun get(x: Int, y: Int): Boolean =
if (x in 0 until height && y in 0 until width) imageData[x * width + y] else defaultValue
operator fun set(x: Int, y: Int, value: Boolean) {
if (x in 0 until height && y in 0 until width) imageData[x * width + y] = value
}
/** Returns the number of set pixels in this slice (ignoring the surrounding infinity). */
fun countSetPixels(): Int = imageData.count { it }
}
/** An advanced image enhancement algorithm able to process slices of infinite images. */
private class ImageEnhancer(enhancementData: BooleanArray) {
private val enhancement = enhancementData.copyOf()
init {
require(enhancement.size == 512) { "Not enough enhancement values." }
}
/** Process the [image] and return a new enhanced copy. */
fun enhance(image: InfiniteImageSlice): InfiniteImageSlice = InfiniteImageSlice(
width = image.width + 2,
height = image.height + 2,
defaultValue = if (enhancement[0]) !image.defaultValue else image.defaultValue,
imageData = BooleanArray((image.width + 2) * (image.height + 2)),
).apply {
for (x in 0 until height) {
for (y in 0 until width) {
val kernelValue = buildString(9) {
for (i in -1..1) {
for (j in -1..1) {
append(if (image[x + i - 1, y + j - 1]) '1' else '0')
}
}
}.toInt(2)
this[x, y] = enhancement[kernelValue]
}
}
}
}
/** Parse the [input] and return the [ImageEnhancer] algorithm instance and the initial [InfiniteImageSlice]. */
private fun parseInput(input: String): Pair<ImageEnhancer, InfiniteImageSlice> = runCatching {
val lines = input.lineSequence().iterator()
val enhancementData = lines.next().map { it == '#' }.toBooleanArray()
lines.next()
var width = -1
var height = 0
val imageData = lines.asSequence()
.onEach { if (width == -1) width = it.length else require(it.length == width) }
.onEach { height++ }
.flatMap { line -> line.map { it == '#' } }
.toList()
.toBooleanArray()
ImageEnhancer(enhancementData) to InfiniteImageSlice(width, height, false, imageData)
}.getOrElse { throw IllegalArgumentException("Invalid input.", it) }
override fun partOne(input: String): Any {
val (enhancement, image) = parseInput(input)
val enhanced = (1..2).fold(image) { acc, _ -> enhancement.enhance(acc) }
return enhanced.countSetPixels()
}
override fun partTwo(input: String): Any {
val (enhancement, image) = parseInput(input)
val enhanced = (1..50).fold(image) { acc, _ -> enhancement.enhance(acc) }
return enhanced.countSetPixels()
}
}