1
0
Files
Advent-of-Code/2020/src/main/kotlin/Day11.kt
T
2020-12-30 18:01:52 +01:00

118 lines
3.4 KiB
Kotlin

package be.vandewalleh.aoc.days
import be.vandewalleh.aoc.utils.input.Day
import be.vandewalleh.aoc.utils.input.Input
import be.vandewalleh.aoc.utils.input.Lines
private typealias Seats = Array<CharArray>
private fun Seats.deepClone(): Seats = map { it.clone() }.toTypedArray()
private operator fun Seats.get(x: Int, y: Int): Char = this[y][x]
private operator fun Seats.set(x: Int, y: Int, value: Char) {
this[y][x] = value
}
private operator fun Seats.contains(xy: Pair<Int, Int>): Boolean {
val (x, y) = xy
return x in 0 until width && y in 0 until height
}
private val Seats.width get() = first().size
private val Seats.height get() = size
private fun Seats.asGridString() = joinToString("\n") { it.joinToString("") }
private fun Seats.countOccupied() = sumBy { it.count { it == '#' } }
@Day(11)
class Day11(@Lines val input: Input<List<String>>) {
private val seats: Seats = input.value.map { it.toCharArray() }.toTypedArray()
private val directions = listOf(
-1 to -1,
+0 to -1,
+1 to -1,
+1 to +0,
+1 to +1,
+0 to +1,
-1 to +1,
-1 to +0,
)
private fun countAdjacentOccupiedSeats(x: Int, y: Int, seats: Array<CharArray>): Int =
directions.mapNotNull { (dx, dy) ->
val xx = x + dx
val yy = y + dy
if (xx to yy in seats) seats[xx, yy]
else null
}.count { it == '#' }
private fun countVisibleOccupiedSeats(x: Int, y: Int, seats: Seats): Int {
var occupied = 0
for ((dx, dy) in directions) {
var xx = x
var yy = y
while (true) {
xx += dx
yy += dy
if (xx to yy !in seats) break
if (seats[xx, yy] == 'L') break
if (seats[xx, yy] == '#') {
occupied++
break
}
}
}
return occupied
}
private fun findLastRepeating(seats: Seats, next: (Seats) -> Seats): Seats {
var previous = seats
val set = HashSet<String>() // use strings because Arrays don't implement equals ??
while (true) {
val res = next(previous)
if (!set.add(res.asGridString())) return res
previous = res
}
}
private fun progress1(previous: Array<CharArray>): Array<CharArray> {
val next = previous.deepClone()
for (x in 0 until seats.width) {
for (y in 0 until seats.height) {
when (previous[x, y]) {
'L' -> if (countAdjacentOccupiedSeats(x, y, previous) == 0) next[x, y] = '#'
'#' -> if (countAdjacentOccupiedSeats(x, y, previous) >= 4) next[x, y] = 'L'
}
}
}
return next
}
fun part1() = findLastRepeating(seats, ::progress1).countOccupied()
private fun progress2(previous: Seats): Array<CharArray> {
val next = previous.deepClone()
for (x in 0 until previous.width) {
for (y in 0 until previous.height) {
when (previous[x, y]) {
'L' -> if (countVisibleOccupiedSeats(x, y, previous) == 0) next[x, y] = '#'
'#' -> if (countVisibleOccupiedSeats(x, y, previous) >= 5) next[x, y] = 'L'
}
}
}
return next
}
fun part2() = findLastRepeating(seats, ::progress2).countOccupied()
}