Day 16: The Floor Will Be Lava

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • cvttsd2si@programming.dev
    link
    fedilink
    arrow-up
    1
    ·
    edit-2
    11 months ago

    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