Skip to content

Commit

Permalink
Solve 2024 day 15 part 2
Browse files Browse the repository at this point in the history
  • Loading branch information
sim642 committed Dec 15, 2024
1 parent 042b3ff commit 4dd8a4f
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 24 deletions.
121 changes: 100 additions & 21 deletions src/main/scala/eu/sim642/adventofcode2024/Day15.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,100 @@ object Day15 {

case class Input(grid: Grid[Char], moves: Seq[Pos])

def simulateMove(grid: Grid[Char], robot: Pos, move: Pos): (Grid[Char], Pos) = {
val newRobot = robot + move
val newBox = Iterator.iterate(newRobot)(_ + move).find(grid(_) != 'O').get
if (grid(newBox) == '.')
(grid.updatedGrid(newBox, 'O').updatedGrid(newRobot, '.'), newRobot)
else
(grid, robot)
trait Part {
def simulateMove(grid: Grid[Char], robot: Pos, move: Pos): (Grid[Char], Pos)
def sumBoxGps(grid: Grid[Char]): Int

def sumMovesBoxGps(input: Input): Int = {
val initialRobot = input.grid.posOf('@')
val initialGrid = input.grid.updatedGrid(initialRobot, '.')
val (finalGrid, finalRobot) = input.moves.foldLeft((initialGrid, initialRobot))({ case ((grid, robot), move) =>
simulateMove(grid, robot, move)
})
sumBoxGps(finalGrid)
}
}

def sumBoxGps(grid: Grid[Char]): Int = {
(for {
(row, y) <- grid.view.zipWithIndex
(cell, x) <- row.view.zipWithIndex
if cell == 'O'
} yield 100 * y + x).sum
object Part1 extends Part {
override def simulateMove(grid: Grid[Char], robot: Pos, move: Pos): (Grid[Char], Pos) = {
val newRobot = robot + move
val newBox = Iterator.iterate(newRobot)(_ + move).find(grid(_) != 'O').get
if (grid(newBox) == '.')
(grid.updatedGrid(newBox, 'O').updatedGrid(newRobot, '.'), newRobot)
else
(grid, robot)
}

override def sumBoxGps(grid: Grid[Char]): Int = {
(for {
(row, y) <- grid.view.zipWithIndex
(cell, x) <- row.view.zipWithIndex
if cell == 'O'
} yield 100 * y + x).sum
}
}

def sumMovesBoxGps(input: Input): Int = {
val initialRobot = input.grid.posOf('@')
val initialGrid = input.grid.updatedGrid(initialRobot, '.')
val (finalGrid, finalRobot) = input.moves.foldLeft((initialGrid, initialRobot))({ case ((grid, robot), move) =>
simulateMove(grid, robot, move)
})
sumBoxGps(finalGrid)
object Part2 extends Part {
def scaleGrid(grid: Grid[Char]): Grid[Char] = {
grid.map(_.flatMap({
case '#' => "##"
case 'O' => "[]"
case '.' => ".."
case '@' => "@."
}))
}

override def simulateMove(grid: Grid[Char], robot: Pos, move: Pos): (Grid[Char], Pos) = {

def helper(grid: Grid[Char], pos: Pos): Option[Grid[Char]] = grid(pos) match {
case '.' => Some(grid)
case '#' => None
case '[' if move.x == 0 => // vertical
for {
newGrid <- helper(grid, pos + move)
newGrid2 <- helper(newGrid, pos + Pos(1, 0) + move)
} yield newGrid2.updatedGrid(pos + move, '[').updatedGrid(pos + Pos(1, 0) + move, ']').updatedGrid(pos, '.').updatedGrid(pos + Pos(1, 0), '.')
case ']' if move.x == 0 => // vertical
for {
newGrid <- helper(grid, pos + move)
newGrid2 <- helper(newGrid, pos - Pos(1, 0) + move)
} yield newGrid2.updatedGrid(pos + move, ']').updatedGrid(pos - Pos(1, 0) + move, '[').updatedGrid(pos, '.').updatedGrid(pos - Pos(1, 0), '.')
case '[' if move.y == 0 => // horizontal
for {
newGrid <- helper(grid, pos + move)
} yield newGrid.updatedGrid(pos + move, '[').updatedGrid(pos, '.')
case ']' if move.y == 0 => // horizontal
for {
newGrid <- helper(grid, pos + move)
} yield newGrid.updatedGrid(pos + move, ']').updatedGrid(pos, '.')
case _ => ???
}

val newRobot = robot + move
helper(grid, newRobot) match {
case Some(newGrid) => (newGrid, newRobot)
case None => (grid, robot)
}
}

override def sumBoxGps(grid: Grid[Char]): Int = {
printGrid(grid)
(for {
(row, y) <- grid.view.zipWithIndex
(cell, x) <- row.view.zipWithIndex
if cell == '['
//gpsX = x min (row.size - 1 - x)
//gpsY = y min (grid.size - 1 - y)
gpsX = x
gpsY = y
() = println((gpsX, gpsY))
} yield 100 * gpsY + gpsX).sum
}

override def sumMovesBoxGps(input: Input): Int = {
val scaledGrid = scaleGrid(input.grid)
super.sumMovesBoxGps(Input(scaledGrid, input.moves))
}
}

def parseGrid(s: String): Grid[Char] = s.linesIterator.map(_.toVector).toVector
Expand All @@ -49,9 +119,18 @@ object Day15 {
case s"$grid\n\n$moves" => Input(parseGrid(grid), parseMoves(moves))
}

def printGrid(grid: Grid[Char]): Unit = {
for (row <- grid) {
for (cell <- row)
print(cell)
println()
}
}

lazy val input: String = scala.io.Source.fromInputStream(getClass.getResourceAsStream("day15.txt")).mkString.trim

def main(args: Array[String]): Unit = {
println(sumMovesBoxGps(parseInput(input)))
println(Part1.sumMovesBoxGps(parseInput(input)))
println(Part2.sumMovesBoxGps(parseInput(input)))
}
}
27 changes: 24 additions & 3 deletions src/test/scala/eu/sim642/adventofcode2024/Day15Test.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,33 @@ class Day15Test extends AnyFunSuite {
|
|<^^>>>vv<v>>v<<""".stripMargin

val exampleInput3 =
"""#######
|#...#.#
|#.....#
|#..OO@#
|#..O..#
|#.....#
|#######
|
|<vv<<^^<<^^""".stripMargin

test("Part 1 examples") {
assert(sumMovesBoxGps(parseInput(exampleInput2)) == 2028)
assert(sumMovesBoxGps(parseInput(exampleInput)) == 10092)
assert(Part1.sumMovesBoxGps(parseInput(exampleInput2)) == 2028)
assert(Part1.sumMovesBoxGps(parseInput(exampleInput)) == 10092)
}

test("Part 1 input answer") {
assert(sumMovesBoxGps(parseInput(input)) == 1476771)
assert(Part1.sumMovesBoxGps(parseInput(input)) == 1476771)
}

test("Part 2 examples") {
assert(Part2.sumMovesBoxGps(parseInput(exampleInput3)) == 618) // from glguy
assert(Part2.sumMovesBoxGps(parseInput(exampleInput2)) == 1751) // from glguy
assert(Part2.sumMovesBoxGps(parseInput(exampleInput)) == 9021)
}

test("Part 2 input answer") {
assert(Part2.sumMovesBoxGps(parseInput(input)) == 1468005)
}
}

0 comments on commit 4dd8a4f

Please sign in to comment.