Skip to content

Instantly share code, notes, and snippets.

@waynejo
Created April 7, 2023 12:21
Show Gist options
  • Save waynejo/10c3d94e55eba288fca3e12a53b69cb5 to your computer and use it in GitHub Desktop.
Save waynejo/10c3d94e55eba288fca3e12a53b69cb5 to your computer and use it in GitHub Desktop.
import java.io.FileInputStream
import scala.annotation.{tailrec, targetName}
import scala.io.StdIn
import scala.language.postfixOps
import scala.math.abs
enum ObjectType:
case stone
case sand
case class Point(x: Int, y: Int)
case class World(element: Map[Point, ObjectType] = Map.empty) {
lazy val minX: Int = element.keys.map(_.x).min
lazy val minY: Int = 0
lazy val maxX: Int = element.keys.map(_.x).max
lazy val maxY: Int = element.keys.filter(key => element(key) == ObjectType.stone).map(_.y).max
def apply(point: Point): Option[ObjectType] =
element.get(point)
def appended(point: Point, objectType: ObjectType): World =
World(element = element + (point -> objectType))
}
def buildWorld(input: Vector[String]): World = {
val points: Vector[Point] = input.flatMap(line => {
line.split(" -> ").map(_.split(",")).sliding(2).flatMap(points => {
val p0x = points(0)(0).toInt
val p0y = points(0)(1).toInt
val p1x = points(1)(0).toInt
val p1y = points(1)(1).toInt
if (p0x == p1x) {
((p0y min p1y) to (p0y max p1y)).map(y => Point(p0x, y)).toVector
} else {
((p0x min p1x) to (p0x max p1x)).map(x => Point(x, p0y)).toVector
}
})
})
points.foldLeft(World()) { (world, point) =>
world.appended(point, ObjectType.stone)
}
}
@tailrec
def computeNewSandPosition(world: World, testPoint: Point = Point(500, 0), endCondition: (World, Point) => Option[Option[Point]]): Option[Point] =
endCondition(world, testPoint) match
case Some(result) =>
result
case _ =>
val bottom = testPoint.copy(y = testPoint.y + 1)
val leftBottom = bottom.copy(x = bottom.x - 1)
val rightBottom = bottom.copy(x = bottom.x + 1)
if world(bottom).isEmpty then
computeNewSandPosition(world, bottom, endCondition)
else if world(leftBottom).isEmpty then
computeNewSandPosition(world, leftBottom, endCondition)
else if world(rightBottom).isEmpty then
computeNewSandPosition(world, rightBottom, endCondition)
else
Some(testPoint)
@tailrec
def simulate(world: World, endCondition: (World, Point) => Option[Option[Point]], step: Int): Int =
computeNewSandPosition(world, endCondition=endCondition) match
case Some(point) =>
simulate(world.appended(point, ObjectType.sand), endCondition, step + 1)
case None =>
step
def solve14_1(input: Vector[String]): Int =
val world = buildWorld(input)
simulate(world, (world, point) => {
if world.maxY < point.y then
Some(None)
else
None
}, 0)
def solve14_2(input: Vector[String]): Int =
val world = buildWorld(input)
simulate(world, (world, point) => {
if world(point).contains(ObjectType.sand) then
Some(None)
else if world.maxY + 1 == point.y then
Some(Some(point))
else
None
}, 0)
@main def solve14(): Unit =
val in = new FileInputStream("example14-2.in")
System.setIn(in)
val inputs = Iterator.continually(StdIn.readLine())
.takeWhile(line => null != line)
.toVector
println(solve14_1(inputs))
println(solve14_2(inputs))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment