generated from Jadarma/advent-of-code-kotlin-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathY2016D08.kt
92 lines (77 loc) · 3.97 KB
/
Y2016D08.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
package aockt.y2016
import aockt.util.OcrDecoder
import aockt.y2016.Y2016D08.Instruction.*
import io.github.jadarma.aockt.core.Solution
object Y2016D08 : Solution {
/** An instruction for building the display. */
private sealed class Instruction {
/** Turns on all of the pixels in a rectangle at the top-left of the screen of the given [width] x [height]. */
data class Rectangle(val width: Int, val height: Int) : Instruction()
/** Shifts all the pixels in the given [row] by [amount] pixels to the right, looping around. */
data class RotateRow(val row: Int, val amount: Int) : Instruction()
/** Shifts all the pixels in the given [column] by [amount] pixels down, looping around. */
data class RotateColumn(val column: Int, val amount: Int) : Instruction()
companion object {
private val rectRegex = Regex("""rect (\d+)x(\d+)""")
private val rowRegex = Regex("""rotate row y=(\d+) by (\d+)""")
private val colRegex = Regex("""rotate column x=(\d+) by (\d+)""")
/** Returns the [Instruction] described by the [input], throwing [IllegalArgumentException] if invalid. */
fun parse(input: String): Instruction {
rectRegex.matchEntire(input)?.destructured?.let { (x, y) -> return Rectangle(x.toInt(), y.toInt()) }
rowRegex.matchEntire(input)?.destructured?.let { (y, v) -> return RotateRow(y.toInt(), v.toInt()) }
colRegex.matchEntire(input)?.destructured?.let { (x, v) -> return RotateColumn(x.toInt(), v.toInt()) }
throw IllegalArgumentException("Invalid input.")
}
}
}
/** The pixel state of a card swiper display. */
private class Display(val width: Int, val height: Int) {
private val grid = Array(height) { BooleanArray(width) { false } }
/** Returns the number of pixels that are currently on. */
fun litCount() = grid.sumOf { row -> row.count { it } }
/** Formats a string according to the display state, meant to be printed. */
fun toDisplayString() = buildString {
grid.forEach { row ->
row.map { if (it) '#' else '.' }.forEach { append(it) }
appendLine()
}
}
/** Mutates the state of this display by applying the given [instruction]. */
fun apply(instruction: Instruction) = when (instruction) {
is Rectangle -> {
for (y in 0 until instruction.height) {
for (x in 0 until instruction.width) {
grid[y][x] = true
}
}
}
is RotateColumn -> {
val offset = instruction.amount % height
val column = instruction.column
val temp = Array(offset) { grid[grid.size - offset + it][column] }
for (row in (grid.size - offset - 1) downTo 0) {
grid[row + offset][column] = grid[row][column]
}
for (row in 0 until offset) {
grid[row][column] = temp[row]
}
}
is RotateRow -> {
val offset = instruction.amount % width
val row = instruction.row
val temp = grid[row].copyOfRange(grid[row].size - offset, grid[row].size)
grid[row].copyInto(grid[row], offset, 0, grid[row].size - offset)
temp.copyInto(grid[row])
Unit
}
}
}
/** Builds the [Display] state by following the instructions contained in the [input]. */
private fun buildDisplay(input: String): Display = Display(50, 6).apply {
input.lineSequence()
.map { Instruction.parse(it) }
.forEach(::apply)
}
override fun partOne(input: String): Int = buildDisplay(input).litCount()
override fun partTwo(input: String): String = buildDisplay(input).toDisplayString().let(OcrDecoder::decode)
}