102 lines
2.9 KiB
Scala
102 lines
2.9 KiB
Scala
package aoc.day10
|
|
|
|
import aoc._
|
|
|
|
type Coord = (Int, Int)
|
|
|
|
extension (c: Coord) def +(other: Coord): Coord = (c._1 + other._1, c._2 + other._2)
|
|
|
|
enum Direction:
|
|
case Up, Down, Left, Right
|
|
|
|
private val directions = Array[Coord]((-1, 0), (1, 0), (0, -1), (0, 1))
|
|
|
|
def apply(current: Coord): Coord = current + directions(ordinal)
|
|
|
|
inline def isHorizontal = this == Left || this == Right
|
|
inline def isVertical = !isHorizontal
|
|
|
|
def next(pipe: Char): Option[Direction] = Some((pipe, this) match
|
|
case ('|', _) if isVertical => this
|
|
case ('-', _) if isHorizontal => this
|
|
case ('L', Down) => Right
|
|
case ('L', Left) => Up
|
|
case ('7', Up) => Left
|
|
case ('7', Right) => Down
|
|
case ('J', Down) => Left
|
|
case ('J', Right) => Up
|
|
case ('F', Up) => Right
|
|
case ('F', Left) => Down
|
|
case _ => return None
|
|
)
|
|
|
|
object Direction:
|
|
val all = Seq(Up, Down, Left, Right)
|
|
def pipe(cur: Direction, next: Direction) =
|
|
Seq('|', '-', '7', 'J', 'F', 'L').find(cur.next(_) == Some(next)).get
|
|
|
|
val board = lines.toArray
|
|
|
|
inline def cell(x: Coord) = board(x._1)(x._2)
|
|
|
|
def inside(c: Coord) =
|
|
val (x, y) = c
|
|
x >= 0 && x < board.length && y >= 0 && y < board(x).length()
|
|
|
|
// part1
|
|
val (loop, sPipe) = {
|
|
val boardSize = board.map(_.length()).sum
|
|
|
|
// look for S
|
|
val sCoord = (0 until board.length).findMap { x =>
|
|
board(x).zipWithIndex.findMap { case (chr, y) => if chr == 'S' then Some((x, y)) else None }
|
|
}.get
|
|
|
|
@scala.annotation.tailrec
|
|
def findCycle(accum: Seq[Coord])(current: Coord, direction: Direction): Option[(Seq[Coord], Direction)] =
|
|
if current == sCoord then Some((accum, direction))
|
|
else if accum.length > boardSize then None
|
|
else
|
|
direction.next(cell(current)) match
|
|
case Some(nx) =>
|
|
val nxCoord = nx(current)
|
|
if inside(nxCoord) then findCycle(current +: accum)(nxCoord, nx) else None
|
|
case None => None
|
|
// find the loop
|
|
Direction.all
|
|
.map(d => (d(sCoord), d))
|
|
.filter(v => inside(v._1))
|
|
.findMap((cur, dir) =>
|
|
findCycle(Seq(sCoord))(cur, dir)
|
|
.map((loop, toS) => (loop.toSet, Direction.pipe(toS, dir)))
|
|
)
|
|
.get
|
|
}
|
|
|
|
def part1 =
|
|
println(loop.size / 2)
|
|
|
|
// part 2
|
|
|
|
@scala.annotation.tailrec
|
|
def loop(accum: Int)(x: Int, y: Int, insideLoop: Boolean): Int =
|
|
if !inside((x, y)) then accum
|
|
else if !loop.contains((x, y)) then loop(accum + (if insideLoop then 1 else 0))(x + 1, y + 1, insideLoop)
|
|
else
|
|
val r = if board(x)(y) == 'S' then sPipe else board(x)(y)
|
|
loop(accum)(
|
|
x + 1,
|
|
y + 1,
|
|
if Seq('|', '-', 'J', 'F').contains(r) then !insideLoop else insideLoop
|
|
)
|
|
|
|
def part2 =
|
|
val res =
|
|
(0 until board.length).map(x => loop(0)(x, 0, false)).sum
|
|
+ (1 until board(0).length).map(y => loop(0)(0, y, false)).sum
|
|
println(res)
|
|
|
|
@main def Day10(part: Int) = part match
|
|
case 1 => part1
|
|
case 2 => part2
|