aoc2023/src/day14.scala

124 lines
2.9 KiB
Scala
Raw Normal View History

2023-12-14 16:31:28 +01:00
package aoc
package day14
import scala.annotation.tailrec
import scala.collection.mutable
val dayNumber = "14"
@main def part1: Unit =
println(part1(loadInput(dayNumber)))
@main def part2: Unit =
println(part2(loadInput(dayNumber)))
def part1(input: String): String =
val lines = input.split('\n')
val border = lines.head.map(_ => '#').mkString + "##"
val grid = lines
.map(l => s"#$l#")
.prepended(border)
.appended(border)
.map(_.to(mutable.ArraySeq))
for
y <- grid.indices
x <- grid.head.indices
do
if grid(y)(x) != 'O' then ()
else
(y - 1 to (0, -1))
.takeWhile(grid(_)(x) == '.')
.lastOption
.foreach(newY =>
grid(y)(x) = '.'
grid(newY)(x) = 'O'
)
grid
.map(_.count(_ == 'O'))
.reverse
.zipWithIndex
.map((count, row) => count * row)
.sum
.toString
def part2(input: String): String =
val lines = input.split('\n')
val border = lines.head.map(_ => '#').mkString + "##"
val grid = lines
.map(l => s"#$l#")
.prepended(border)
.appended(border)
.map(_.to(mutable.ArraySeq))
val coords =
for
y <- grid.indices
x <- grid.head.indices
yield (x, y)
enum Directions(
val coordinates: Seq[(Int, Int)],
val rangeToEdge: (Int, Int) => Seq[(Int, Int)]
):
case North
extends Directions(
coords,
(x, y) => (y - 1 to (0, -1)).map((x, _))
)
case West
extends Directions(
coords.sortBy(_._1),
(x, y) => (x - 1 to (0, -1)).map((_, y))
)
case South
extends Directions(
coords.reverse,
(x, y) => (y + 1 until grid.length).map((x, _))
)
case East
extends Directions(
coords.sortBy(_._1).reverse,
(x, y) => (x + 1 until grid.length).map((_, y))
)
def cycle =
for d <- Directions.values do
for (x, y) <- d.coordinates do
if grid(y)(x) != 'O' then ()
else
d.rangeToEdge(x, y)
.takeWhile((xR, yR) => grid(yR)(xR) == '.')
.lastOption
.foreach((xR, yR) =>
grid(y)(x) = '.'
grid(yR)(xR) = 'O'
)
def cycleUntilRepeat: (Int, Int) =
val seen = mutable.Map.empty[String, Int]
@tailrec def aux(iter: Int): (Int, Int) =
cycle
val gridString = grid.map(_.mkString).mkString("\n")
seen.get(gridString) match
case Some(prevIterations) => (iter, iter - prevIterations)
case None =>
seen.put(gridString, iter)
aux(iter + 1)
aux(1)
val (repeatedIndex, repeatedLength) = cycleUntilRepeat
val cyclesLeft = (1_000_000_000 - repeatedIndex) % repeatedLength
for _ <- (0 until cyclesLeft) do cycle
grid
.map(_.count(_ == 'O'))
.reverse
.zipWithIndex
.map((count, row) => count * (row))
.sum
.toString