aoc2023/Day14.scala

70 lines
1.8 KiB
Scala
Raw Normal View History

2023-12-14 15:12:09 +00:00
package aoc.day14
import aoc._
import scala.collection.mutable
type Board = Seq[Seq[Char]]
val board = lines.map(_.toSeq).toSeq
enum Direction { case Up, Down, Left, Right }
import Direction._
extension (board: Board)
inline def doTiltLeft(inline before: Board => Board, inline after: Board => Board): Board =
after(before(board).map { col =>
col
.splitBy(_ == '#') // split by tilted rocks
.map { group =>
val (rocks, empty) = group.partition(_ == 'O')
(rocks ++ empty).mkString
}
.mkString("#")
.toSeq
})
def doTilt(direction: Direction): Board = direction match
case Up => doTiltLeft(_.transpose, _.transpose)
case Down => doTiltLeft(_.transpose.map(_.reverse), _.map(_.reverse).transpose)
case Left => doTiltLeft(identity, identity)
case Right => doTiltLeft(_.map(_.reverse), _.map(_.reverse))
def cycle = Seq(Up, Left, Down, Right).foldLeft(board)(_.doTilt(_))
def load =
board.reverse // rows in reverse
.zipWithIndex.map { case (row, idx) => row.count(_ == 'O') * (idx + 1L) }.sum
// part 1
def part1 =
println(board.doTilt(Up).load)
// part 2
object Cycling:
private val memo = mutable.Map.empty[(Board, Int), Board]
private def getPower(b: Board, p: Int): Board = // b.cycle 2^p times
memo.getOrElseUpdate(
(b, p), {
if p == 0 then b.cycle
else getPower(getPower(b, p - 1), p - 1)
}
)
def apply(b: Board, times: Long): Board =
@scala.annotation.tailrec
def loop(b: Board, i: Int): Board =
val pow = 1L << i
if pow > times then b
else if (pow & times) > 0 then loop(getPower(b, i), i + 1)
else loop(b, i + 1)
loop(b, 0)
def part2 =
println(Cycling(board, 1_000_000_000).load)
@main def Day14(part: Int) = part match
case 1 => part1
case 2 => part2