Skip to main content

Day 14: Regolith Reservoir

Puzzle description

https://adventofcode.com/2022/day/14

Final Solution

def part1(input: String): Int =
val search = parseInput(input)
search.states
.takeWhile(_.fallingPath.head.y < search.lowestRock)
.last
.sand
.size

def part2(input: String): Int =
parseInput(input).states.last.sand.size

def 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

Solutions from the community

Share your solution to the Scala community by editing this page. You can even write the whole article! See here for the expected format