# Day 14: Regolith Reservoir

## Puzzle description​

def part1(input: String): Int =  val search = parseInput(input)  search.states    .takeWhile(_.fallingPath.head.y < search.lowestRock)    .last    .sand    .sizedef part2(input: String): Int =  parseInput(input).states.last.sand.sizedef parseInput(input: String): Scan =  val paths = input.linesIterator    .map { line =>      line.split(" -> ").map { case s"$x,$y" => Point(x.toInt, y.toInt) }.toList    }  val rocks = paths.flatMap { path =>    path.sliding(2).flatMap {      case List(p1, p2) =>        val dx = p2.x - p1.x        val dy = p2.y - p1.y        if dx == 0 then (p1.y to p2.y by dy.sign).map(Point(p1.x, _))        else (p1.x to p2.x by dx.sign).map(Point(_, p1.y))      case _ => None    }  }.toSet  Scan(rocks)case class Point(x: Int, y: Int)case class Scan(rocks: Set[Point]):  val lowestRock = rocks.map(_.y).max  val floor = lowestRock + 2  case class State(fallingPath: List[Point], sand: Set[Point]):    def isFree(p: Point) = !sand(p) && !rocks(p)    def next: Option[State] = fallingPath.headOption.map {      case sandUnit @ Point(x, y) =>        val down = Some(Point(x, y + 1)).filter(isFree)        val downLeft = Some(Point(x - 1, y + 1)).filter(isFree)        val downRight = Some(Point(x + 1, y + 1)).filter(isFree)        down.orElse(downLeft).orElse(downRight).filter(_.y < floor) match          case Some(fallingPos) =>            State(fallingPos :: fallingPath, sand)          case None =>            State(fallingPath.tail, sand + sandUnit)    }  def states: LazyList[State] =    val source = Point(500, 0)    LazyList.unfold(State(List(source), Set.empty)) { _.next.map(s => s -> s) }end Scan