package com.adventofcode.y2018 import com.adventofcode.y2018.utils.Index2 import com.adventofcode.y2018.utils.get import com.soywiz.kds.Array2 import com.soywiz.kds.Queue import kotlin.math.max import kotlin.math.min // https://adventofcode.com/2018/day/17 fun solve_day17_part1(inputs: List): Int { return solve(inputs, Tile.Fall, Tile.Still) } fun solve_day17_part2(inputs: List): Int { return solve(inputs, Tile.Still) } private fun solve(inputs: List, vararg tiles: Tile): Int { val size = 2000 val field = Array2(size, size, Tile.Empty) var minY = Int.MAX_VALUE var maxY = 0 for (input in inputs) { val i = input.substringAfter("=").substringBefore(",").toInt() val j1 = input.substringAfterLast("=").substringBefore("..").toInt() val j2 = input.substringAfter("..").toInt() for (j in j1..j2) { if (input.startsWith("x")) { field[i, j] = Tile.Wall minY = min(minY, j) maxY = max(maxY, j) } else { field[j, i] = Tile.Wall minY = min(minY, i) maxY = max(maxY, i) } } } val queue = Queue(Index2(500, 0)) fun fallDown(from: Index2): Index2? { if (field[from] == Tile.Still) { // already processed return null } val x = from.x for (y in from.y..maxY) { when(field[x, y]) { // hit ground Tile.Wall, Tile.Still -> return Index2(x, y - 1) // already processed Tile.Fall -> return null // continue falling Tile.Empty -> field[x, y] = Tile.Fall } } // falling forever return null } fun flowToSide(from: Index2, inc: (Int) -> Int): Index2? { var (x, y) = from while (true) { field[x, y] = Tile.Fall if (field[x, y + 1] !in listOf(Tile.Wall, Tile.Still)) { // falling queue.enqueue(Index2(x, y + 1)) return null } if (field[inc(x), y] == Tile.Wall) { // settle return Index2(x, y) } x = inc(x) } } while (queue.isNotEmpty()) { val cur = queue.dequeue() val ground = fallDown(cur) ?: continue var (x, y) = ground while (true) { val settleLeft = flowToSide(Index2(x, y), Int::dec) val settleRight = flowToSide(Index2(x, y), Int::inc) if (settleLeft == null || settleRight == null) break // settle (settleLeft.x..settleRight.x).forEach { field[it, y] = Tile.Still } y-- } } return (minY..maxY).sumBy { y -> (0 until size).sumBy { x -> if (field[x, y] in tiles) 1 else 0 } } } private enum class Tile { Empty, Wall, Fall, Still }