Day 17
This commit is contained in:
175
src/main/kotlin/com/basdado/adventofcode/Day17.kt
Normal file
175
src/main/kotlin/com/basdado/adventofcode/Day17.kt
Normal file
@@ -0,0 +1,175 @@
|
||||
package com.basdado.adventofcode
|
||||
|
||||
import kotlin.math.max
|
||||
|
||||
val DAY17_INPUT = "/day/17/input.txt"
|
||||
|
||||
fun main() {
|
||||
|
||||
val day = Day17()
|
||||
day.puzzle1()
|
||||
day.puzzle2()
|
||||
}
|
||||
|
||||
class Day17 {
|
||||
|
||||
fun puzzle1() {
|
||||
|
||||
val ground = parseInput()
|
||||
|
||||
flowWaterFrom(ground, 500, 0)
|
||||
|
||||
println(ground.ground.filter { x -> x == GroundType.FLOWING_WATER || x == GroundType.STILL_WATER }.count())
|
||||
}
|
||||
|
||||
fun puzzle2() {
|
||||
|
||||
val ground = parseInput()
|
||||
flowWaterFrom(ground, 500, 0)
|
||||
|
||||
println(ground.ground.filter { x -> x == GroundType.STILL_WATER }.count())
|
||||
}
|
||||
|
||||
private fun flowWaterFrom(ground: Ground, startX: Int, startY: Int) {
|
||||
|
||||
// println("Flowing from $startX, $startY")
|
||||
// println(ground)
|
||||
|
||||
var flowStartedLastRound = false
|
||||
while(true) {
|
||||
|
||||
// println(ground)
|
||||
|
||||
var y = max(startY, ground.startY)
|
||||
var x = startX
|
||||
|
||||
if (!ground.waterCanPenetrate(x, y)) {
|
||||
return
|
||||
}
|
||||
|
||||
while (y + 1 < ground.startY + ground.height && ground.waterCanPenetrate(x, y + 1)) {
|
||||
y++
|
||||
}
|
||||
val firstGroundHit = Vector2(x, y)
|
||||
if (y >= ground.startY + ground.height - 1 || ground.get(x, y + 1) == GroundType.FLOWING_WATER) {
|
||||
// If we hit flowing water or the end of the ground, we're done, the water flows from the source to here:
|
||||
for (waterFlowY in max(startY, ground.startY)..y) {
|
||||
ground.set(x, waterFlowY, GroundType.FLOWING_WATER)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// let's check if we're in a pool-type thing:
|
||||
while (ground.waterCanPenetrate(x, y) && ground.waterCanFlowOn(x, y + 1)) {
|
||||
x++
|
||||
}
|
||||
val wallRight = ground.get(x, y) == GroundType.CLAY
|
||||
val wallRightX = x
|
||||
x = firstGroundHit.x
|
||||
y = firstGroundHit.y
|
||||
while (ground.waterCanPenetrate(x, y) && ground.waterCanFlowOn(x, y + 1)) {
|
||||
x--
|
||||
}
|
||||
val wallLeft = ground.get(x, y) == GroundType.CLAY
|
||||
val wallLeftX = x
|
||||
|
||||
// If we're in a pool-type thing, we're dealing with still water
|
||||
// If we're not in a pool-type thing, we're dealing with flowing water
|
||||
|
||||
if (wallLeft && wallRight) {
|
||||
((wallLeftX + 1)..(wallRightX - 1)).forEach { waterX -> ground.set(waterX, y, GroundType.STILL_WATER) }
|
||||
flowStartedLastRound = false
|
||||
} else if (!flowStartedLastRound) {
|
||||
if (!wallLeft) {
|
||||
flowWaterFrom(ground, wallLeftX, y)
|
||||
flowStartedLastRound = true
|
||||
}
|
||||
if (!wallRight) {
|
||||
flowWaterFrom(ground, wallRightX, y)
|
||||
flowStartedLastRound = true
|
||||
}
|
||||
} else {
|
||||
((wallLeftX + 1)..(wallRightX - 1)).forEach { waterX -> ground.set(waterX, y, GroundType.FLOWING_WATER) }
|
||||
flowStartedLastRound = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseInput(): Ground {
|
||||
|
||||
val scannerRegex = Regex("""([xy])=(\d+),\s*([xy])=(\d+)..(\d+)""")
|
||||
val clayCoordinates = mutableSetOf<Vector2>()
|
||||
lines(DAY17_INPUT)
|
||||
.map { scannerRegex.matchEntire(it)!! }
|
||||
.forEach{
|
||||
if (it.groupValues[1] == "x") {
|
||||
val x = it.groupValues[2].toInt()
|
||||
assert(it.groupValues[3] == "y")
|
||||
for (y in it.groupValues[4].toInt()..it.groupValues[5].toInt()) {
|
||||
clayCoordinates.add(Vector2(x, y))
|
||||
}
|
||||
} else {
|
||||
val y = it.groupValues[2].toInt()
|
||||
assert(it.groupValues[3] == "x")
|
||||
for (x in it.groupValues[4].toInt()..it.groupValues[5].toInt()) {
|
||||
clayCoordinates.add(Vector2(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val minX = clayCoordinates.map { it.x }.min()!!
|
||||
val maxX = clayCoordinates.map { it.x }.max()!!
|
||||
val minY = clayCoordinates.map { it.y }.min()!!
|
||||
val maxY = clayCoordinates.map { it.y }.max()!!
|
||||
val width = maxX - minX + 1
|
||||
val height = maxY - minY + 1
|
||||
|
||||
val ground = Ground(width, height, minX, minY)
|
||||
|
||||
clayCoordinates.forEach{ ground.set(it.x, it.y, GroundType.CLAY )}
|
||||
|
||||
return ground
|
||||
}
|
||||
|
||||
enum class GroundType(val char: kotlin.Char) {
|
||||
CLAY('#'), SAND('.'), STILL_WATER('~'), FLOWING_WATER('|')
|
||||
}
|
||||
|
||||
data class Vector2(val x: Int, val y: Int)
|
||||
|
||||
class Ground (val width: Int, val height: Int, val startX: Int, val startY: Int) {
|
||||
|
||||
var ground: Array<GroundType> = Array(width * height) {GroundType.SAND}
|
||||
|
||||
fun index(x: Int, y: Int): Int = (y - startY) * width + (x - startX)
|
||||
|
||||
fun set(x: Int, y: Int, groundType: GroundType) {
|
||||
ground[index(x, y)] = groundType
|
||||
}
|
||||
|
||||
fun get (x: Int, y: Int): GroundType = ground[index(x, y)]
|
||||
|
||||
fun waterCanPenetrate(x: Int, y: Int): Boolean {
|
||||
val type = get(x, y)
|
||||
return type == GroundType.SAND
|
||||
}
|
||||
|
||||
fun waterCanFlowOn(x: Int, y: Int): Boolean {
|
||||
val type = get(x, y)
|
||||
return type == GroundType.CLAY || type == GroundType.STILL_WATER
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
||||
val res = StringBuilder()
|
||||
for (y in startY until (startY + height)) {
|
||||
for (x in startX until (startX + width)) {
|
||||
res.append(get(x, y).char)
|
||||
}
|
||||
res.append("\r\n")
|
||||
}
|
||||
return res.toString()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
8
src/main/resources/day/17/example.txt
Normal file
8
src/main/resources/day/17/example.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
x=495, y=2..7
|
||||
y=7, x=495..501
|
||||
x=501, y=3..7
|
||||
x=498, y=2..4
|
||||
x=506, y=1..2
|
||||
x=498, y=10..13
|
||||
x=504, y=10..13
|
||||
y=13, x=498..504
|
||||
8
src/main/resources/day/17/example2.txt
Normal file
8
src/main/resources/day/17/example2.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
x=488, y=1..2
|
||||
x=510, y=1..2
|
||||
x=495, y=2..7
|
||||
x=505, y=2..7
|
||||
y=7, x=495..505
|
||||
x=490, y=10..13
|
||||
x=500, y=10..13
|
||||
y=13, x=490..500
|
||||
1788
src/main/resources/day/17/input.txt
Normal file
1788
src/main/resources/day/17/input.txt
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user