17. den byl o simulaci toku vody a napouštění do různých nádržích pod sebou. Nicméně ačkoliv mi voda tekla celkem pěkně, správnou odpověď jsem nezískal. Můj časový limit pro dnešek vypršel a tak si opět počkám, co bude zítra 🙂 . Můj kód, na kterém jsem skončil je zde:
import com.ichipsea.kotlin.matrix.createMutableMatrix
import com.ichipsea.kotlin.matrix.toList
import com.svetylkovo.adventofcode2018.day17.SeekerDirection.*
import com.svetylkovo.adventofcode2018.day17.SoilType.*
import com.svetylkovo.adventofcode2018.toNonSeparatedPrettyString
import com.svetylkovo.rojo.Rojo
import java.io.File
import kotlin.streams.toList
enum class SeekerDirection {
LEFT, RIGHT, DOWN, UP
}
enum class SoilType(val sign: Char) {
CLAY('#'), SAND('.'), SPRING('+'), WATER('W');
override fun toString() = "$sign"
}
data class ClaySquare(var x: Int, var y: Int)
class WaterSimulator(
val width: Int,
val height: Int,
val springX: Int,
val springY: Int,
val claySquares: List<ClaySquare>
) {
var seekStep = 0
var seekerX = springX
var seekerY = springY
var seekerDirection = DOWN
val flowBranches = mutableListOf<Pair<Int, Int>>()
val leftFallMap = mutableMapOf<Int, MutableList<Pair<Int, Int>>>()
val rightFallMap = mutableMapOf<Int, MutableList<Pair<Int, Int>>>()
val matrix = createMutableMatrix(width, height) { x, y ->
when {
x == springX && y == springY -> SPRING
claySquares.any { it.x == x && it.y == y } -> CLAY
else -> SAND
}
}
fun flow() {
seekWaterPath()
while (flowBranches.isNotEmpty()) {
val nextCoords = flowBranches.removeAt(0)
seekerX = nextCoords.first
seekerY = nextCoords.second
seekerDirection = RIGHT
seekWaterPath()
}
}
val currentCoords get() = seekerX to seekerY
private fun seekWaterPath() {
seekStep++
mainLoop@
while (seekerX < width - 1 && seekerY < height - 1) {
when (seekerDirection) {
LEFT -> {
if (isAtLeftFallOfDifferentStep()) {
break@mainLoop
}
when {
matrix[seekerX, seekerY + 1] == SAND -> {
seekerDirection = DOWN
val leftFall = leftFallMap.computeIfAbsent(seekStep) { mutableListOf() }
leftFall += currentCoords
}
matrix[seekerX - 1, seekerY] == CLAY -> seekerDirection = RIGHT
else -> seekerX--
}
}
RIGHT -> {
if (isAtRightFallOfDifferentStep()) {
break@mainLoop
}
when {
matrix[seekerX, seekerY + 1] == SAND -> {
seekerDirection = DOWN
val rightFall = rightFallMap.computeIfAbsent(seekStep) { mutableListOf() }
rightFall += currentCoords
}
matrix[seekerX + 1, seekerY] == CLAY -> seekerDirection = UP
else -> seekerX++
}
}
DOWN -> {
when (matrix[seekerX, seekerY + 1]) {
WATER -> {
seekerDirection = LEFT
if (matrix[seekerX + 1, seekerY] != CLAY || matrix[seekerX + 1, seekerY + 1] != CLAY) seekerY++
}
CLAY -> seekerDirection = LEFT
else -> seekerY++
}
}
UP -> {
seekerDirection = LEFT
seekerY--
if (isAtLeftFallOfDifferentStep() && matrix[seekerX - 1, seekerY] != CLAY) seekerX--
if (matrix[seekerX + 1, seekerY] == SAND) {
flowBranches += seekerX to seekerY
}
}
}
markCurrentAsWater()
}
}
private fun isAtLeftFallOfDifferentStep() = leftFallMap
.entries
.find { currentCoords in it.value }
?.let { it.key != seekStep }
?: false
private fun isAtRightFallOfDifferentStep() = rightFallMap
.entries
.find { currentCoords in it.value }
?.let { it.key != seekStep }
?: false
private fun markCurrentAsWater() {
matrix[seekerX, seekerY] = WATER
}
fun coutWaterSquares() = matrix.toList().count { it == WATER }
override fun toString() = matrix.toNonSeparatedPrettyString()
companion object {
fun fromInput(input: String, springX: Int, springY: Int): WaterSimulator {
val clays = Rojo.map("(\\w)=(\\d+), (\\w)=(\\d+)..(\\d+)", input) { coordA, valueA, coordB, fromB, toB ->
if (coordA == "x") (fromB.toInt()..toB.toInt()).map { y -> ClaySquare(valueA.toInt(), y) }
else (fromB.toInt()..toB.toInt()).map { x -> ClaySquare(x, valueA.toInt()) }
}.toList().flatten().distinctBy { it.x to it.y }
val xOffset = (clays.map { it.x } + springX).min() ?: 0
val yOffset = (clays.map { it.y } + springY).min() ?: 0
clays.forEach {
it.x -= xOffset
it.y -= yOffset
}
val offsetSpringX = springX - xOffset
val offsetSpringY = springY - yOffset
val width = (clays.map { it.x } + offsetSpringX).max()?.plus(1) ?: 0
val height = (clays.map { it.y } + offsetSpringY).max()?.plus(1) ?: 0
return WaterSimulator(width, height, offsetSpringX, offsetSpringY, clays)
}
}
}
fun main() {
val input = File("input17.txt").readText()
val waterSimulator = WaterSimulator.fromInput(input, 500, 0)
waterSimulator.flow()
println(waterSimulator.coutWaterSquares())
println(waterSimulator.toString())
}