11package  eu .sim642 .adventofcode2024 
22
33import  eu .sim642 .adventofcodelib .Grid 
4- import  eu .sim642 .adventofcodelib .graph .{BFS , GraphSearch , GraphTraversal , Heuristic , SimultaneousBFS , TargetNode , UnitNeighbors }
5- import  eu .sim642 .adventofcodelib .pos .Pos 
64import  eu .sim642 .adventofcodelib .GridImplicits .* 
75import  eu .sim642 .adventofcodelib .box .Box 
6+ import  eu .sim642 .adventofcodelib .graph .* 
7+ import  eu .sim642 .adventofcodelib .pos .Pos 
88
99import  scala .collection .mutable 
1010
@@ -46,7 +46,7 @@ object Day21 {
4646
4747    case  class  State (directionalPoss : List [Pos ], numericPos : Pos , input : Code ) {
4848
49-       def  numericPress (button : Char ):  Option [State ] =  button match  {
49+       private   def  numericPress (button : Char ):  Option [State ] =  button match  {
5050        case  'A'  => 
5151          val  newButton  =  numericKeypad(numericPos)
5252          Some (copy(input =  input +  newButton))
@@ -59,7 +59,7 @@ object Day21 {
5959            None  //  out of keypad
6060      }
6161
62-       def  directionalPress (button : Char ):  Option [State ] =  directionalPoss match  {
62+       private   def  directionalPress (button : Char ):  Option [State ] =  directionalPoss match  {
6363        case  Nil  =>  numericPress(button)
6464        case  directionalPos ::  newDirectionalPoss => 
6565          button match  {
@@ -84,9 +84,11 @@ object Day21 {
8484    override  def  shortestSequenceLength (code : Code , directionalKeypads : Int ):  Long  =  {
8585
8686      val  graphSearch  =  new  GraphSearch [State ] with  UnitNeighbors [State ] {
87-         override  val  startNode :  State  =  State (List .fill(directionalKeypads)(directionalKeypad.posOf('A' )), numericKeypad.posOf('A' ), " "  )
87+         override  val  startNode :  State  = 
88+           State (List .fill(directionalKeypads)(directionalKeypad.posOf('A' )), numericKeypad.posOf('A' ), " "  )
8889
89-         override  def  unitNeighbors (state : State ):  IterableOnce [State ] =  " <v>^A"  .iterator.flatten(state.userPress).filter(s =>  code.startsWith(s.input))
90+         override  def  unitNeighbors (state : State ):  IterableOnce [State ] = 
91+           " <v>^A"  .iterator.flatten(state.userPress).filter(newState =>  code.startsWith(newState.input))
9092
9193        override  def  isTargetNode (state : State , dist : Int ):  Boolean  =  state.input ==  code
9294      }
@@ -110,14 +112,18 @@ object Day21 {
110112      }
111113    }
112114
115+     private  val  offsetDirectionals :  Map [Pos , Char ] =  directionalOffsets.map(_.swap)
116+ 
113117    private  def  keypadPaths (keypad : Grid [Char ]):  Map [(Char , Char ), Set [Code ]] =  {
114118      val  box  =  Box (Pos .zero, Pos (keypad(0 ).size -  1 , keypad.size -  1 ))
119+       //  TODO: use one traversal per position (to all other positions), instead of pairwise
115120      (for  {
116121        startPos <-  box.iterator
117122        if  keypad(startPos) !=  ' ' 
118123        targetPos <-  box.iterator
119124        if  keypad(targetPos) !=  ' ' 
120125      } yield  {
126+         //  TODO: use multi-predecessor BFS to construct all shortest paths
121127        val  graphSearch  =  new  GraphSearch [Pos ] with  UnitNeighbors [Pos ] with  TargetNode [Pos ] {
122128          override  val  startNode :  Pos  =  startPos
123129
@@ -132,7 +138,7 @@ object Day21 {
132138            .filter(_.head ==  targetPos)
133139            .map(poss => 
134140              (poss lazyZip poss.tail)
135-                 .map({ case   (p2, p1) =>  directionalOffsets.find(_._2  ==   p1 -  p2).get._1  })
141+                 .map({ (p2, p1) =>  offsetDirectionals( p1 -  p2) })
136142                .mkString
137143            )
138144            .toSet
@@ -142,26 +148,25 @@ object Day21 {
142148    private  val  numericPaths :  Map [(Char , Char ), Set [Code ]] =  keypadPaths(numericKeypad)
143149    private  val  directionalPaths :  Map [(Char , Char ), Set [Code ]] =  keypadPaths(directionalKeypad)
144150
151+     def  keypadPaths (keypad : Int ):  Map [(Char , Char ), Set [Code ]] =  if  (keypad ==  0 ) numericPaths else  directionalPaths
152+ 
145153    override  def  shortestSequenceLength (code : Code , directionalKeypads : Int ):  Long  =  {
146154      val  memo  =  mutable.Map .empty[(Code , Int ), Long ]
147155
148-       def  helper (code : Code , i : Int ):  Long  =  {
149-         memo.getOrElseUpdate((code, i), {
150-           // assert(directionalKeypads == 0)
151-           code.foldLeft(('A' , 0L ))({ case  ((prev, length), cur) => 
152-             val  newLength  = 
153-               (for  {
154-                 path <-  if  (i ==  0 ) numericPaths((prev, cur)) else  directionalPaths((prev, cur))
155-                 path2 =  path +  'A' 
156-                 len = 
157-                   if  (i ==  directionalKeypads)
158-                     path2.length.toLong
159-                   else 
160-                     helper(path2, i +  1 )
161-               } yield  len).min
162-             (cur, length +  newLength)
163-           })._2
164-         })
156+       def  helper (code : Code , keypad : Int ):  Long  =  {
157+         if  (keypad ==  directionalKeypads +  1 )
158+           code.length
159+         else  {
160+           memo.getOrElseUpdate((code, keypad), {
161+             ((" A"   +  code) lazyZip code) //  start moving from A
162+               .map({ (prev, cur) => 
163+                 keypadPaths(keypad)((prev, cur))
164+                   .map(path =>  helper(path +  'A' , keypad +  1 )) //  end at A to press
165+                   .min
166+               })
167+               .sum
168+           })
169+         }
165170      }
166171
167172      helper(code, 0 )
@@ -176,7 +181,7 @@ object Day21 {
176181  val  part2DirectionalKeypads  =  25 
177182
178183  def  main (args : Array [String ]):  Unit  =  {
179-     import  DynamicProgrammingSolution ._ 
184+     import  DynamicProgrammingSolution .* 
180185    println(sumCodeComplexity(parseCodes(input), part1DirectionalKeypads))
181186    println(sumCodeComplexity(parseCodes(input), part2DirectionalKeypads))
182187
0 commit comments