118 lines
3.4 KiB
Kotlin
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()
|
|
|
|
}
|