package be.vandewalleh.aoc.days import be.vandewalleh.aoc.utils.input.Day import be.vandewalleh.aoc.utils.input.Lines private typealias Seats = Array 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): 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: List) { private val seats: Seats = input.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): 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() // 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): Array { 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 { 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() }