-
-
Save waynejo/10c3d94e55eba288fca3e12a53b69cb5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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