Scala3
This could be much more efficient (and quite a bit shorter), but I wanted to try out the scala-graph library (https://www.scala-graph.org)
import day10._
import day10.Dir._
import scalax.collection.edges.DiEdge
import scalax.collection.immutable.Graph
import scalax.collection.edges.DiEdgeImplicits
import scalax.collection.generic.AnyEdge
import scalax.collection.generic.Edge
case class Node(ps: Set[Pos])
def getNode(p: Pos, d: Dir) = Node(Set(p, walk(p, d)))
def connect(p: Pos, d1: Dir, d2: Dir) = List(getNode(p, d1) ~> getNode(p, d2), getNode(p, d2) ~> getNode(p, d1))
def parseGrid(a: List[List[Char]]) =
def parseCell(s: Char, pos: Pos) =
s match
case '.' => connect(pos, Left, Right) ++ connect(pos, Up, Down)
case '/' => connect(pos, Left, Up) ++ connect(pos, Right, Down)
case '\\' => connect(pos, Left, Down) ++ connect(pos, Right, Up)
case '-' => connect(pos, Left, Right) ++ List(
getNode(pos, Up) ~> getNode(pos, Left), getNode(pos, Up) ~> getNode(pos, Right),
getNode(pos, Down) ~> getNode(pos, Left), getNode(pos, Down) ~> getNode(pos, Right),
)
case '|' => connect(pos, Up, Down) ++ List(
getNode(pos, Left) ~> getNode(pos, Up), getNode(pos, Left) ~> getNode(pos, Down),
getNode(pos, Right) ~> getNode(pos, Up), getNode(pos, Right) ~> getNode(pos, Down),
)
case _ => List().ensuring(false)
val edges = a.zipWithIndex.flatMap((r, y) => r.zipWithIndex.map((v, x) => v -> Pos(x, y))).map(parseCell).reduceLeft((a, b) => a ++ b)
Graph() ++ edges
def illuminationFrom(p: Pos, d: Dir, g: Graph[Node, DiEdge[Node]], inBounds: Pos => Boolean): Long =
val es = getNode(p, d.opposite) ~> getNode(p, d)
val g2 = g + es
val n = g2.get(getNode(p, d))
n.outerNodeTraverser.flatMap(_.ps).toSet.filter(inBounds).size
def inBounds(a: List[String])(p: Pos) = p.x >= 0 && p.x < a(0).size && p.y >= 0 && p.y < a.size
def task1(a: List[String]): Long =
illuminationFrom(Pos(-1, 0), Right, parseGrid(a.map(_.toList)), inBounds(a))
def task2(a: List[String]): Long =
val inits = (for y <- a.indices yield Seq((Pos(-1, y), Right), (Pos(a(y).size, y), Left)))
++ (for x <- a(0).indices yield Seq((Pos(x, -1), Down), (Pos(x, a.size), Up)))
val g = parseGrid(a.map(_.toList))
inits.flatten.map((p, d) => illuminationFrom(p, d, g, inBounds(a))).max