@@ -5,7 +5,6 @@ import scala.collection.parallel.CollectionConverters.*
55import scala .collection .immutable .Range .Inclusive
66import prelude .time
77
8- extension [A ](x : A ) inline def tap [B ](f : A => B ): A = { f(x); x }
98extension (x : Int ) inline def ± (y : Int ) = x - y to x + y
109extension (x : Inclusive )
1110 inline def & (y : Inclusive ) = (x.start max y.start) to (x.end min y.end)
@@ -28,48 +27,46 @@ object Pos:
2827 inline def + (q : Pos ): Pos = (p.x + q.x, p.y + q.y)
2928 inline infix def taxiDist (q : Pos ) = (p.x - q.x).abs + (p.y - q.y).abs
3029
31- def entries (input : String ): Iterator [(Char , Pos )] = for
32- (line, y) <- input.linesIterator.zipWithIndex
33- (c, x) <- line.iterator.zipWithIndex
34- yield c -> Pos (x, y)
30+ case class Rect (x : Inclusive , y : Inclusive ):
31+ inline def & (that : Rect ) = Rect (x & that.x, y & that.y)
3532
36- case class Size (width : Int , height : Int ):
37- val widthRange = 0 to width - 1
38- val heightRange = 0 to height - 1
33+ def iterator : Iterator [Pos ] = for
34+ y <- y.iterator
35+ x <- x.iterator
36+ yield Pos (x, y)
3937
4038object Track :
4139 def parse (input : String ) =
42- val lines = input.split('\n ' )
43- val size = Size ( lines.head.size, lines.size)
44- entries(input).foldLeft( Track (Pos .zero, Pos .zero, Set .empty, size)) {
45- case (track, (c, p) ) =>
46- c match
47- case 'S' => track.copy(start = p)
48- case 'E' => track.copy(end = p)
49- case '#' => track.copy(walls = track.walls + p)
50- case _ => track
40+ val lines = input.trim. split('\n ' )
41+ val bounds = Rect ( 0 to lines.head.size - 1 , 0 to lines.size - 1 )
42+ val track = Track (Pos .zero, Pos .zero, Set .empty, bounds)
43+ bounds.iterator.foldLeft(track) { (track, p ) =>
44+ lines(p.y)(p.x) match
45+ case 'S' => track.copy(start = p)
46+ case 'E' => track.copy(end = p)
47+ case '#' => track.copy(walls = track.walls + p)
48+ case _ => track
5149 }
5250
53- case class Track (start : Pos , end : Pos , walls : Set [Pos ], bounds : Size ):
51+ case class Track (start : Pos , end : Pos , walls : Set [Pos ], bounds : Rect ):
5452 lazy val path : Vector [Pos ] =
55- val visited = collection.mutable. Set (start)
56- inline def canMove ( p : Pos ) = ! walls.contains(p) && ! visited.contains (p)
53+ inline def canMove ( prev : List [ Pos ])( p : Pos ) =
54+ ! walls.contains(p) && Some (p) != prev.headOption
5755
58- @ tailrec def go (xs : List [Pos ]): List [Pos ] = xs.head match
59- case p if p == end => xs
60- case p => go(p.neighbors.filter(canMove).tap(visited ++= _).head :: xs)
56+ @ tailrec def go (xs : List [Pos ]): List [Pos ] = xs match
57+ case Nil => Nil
58+ case p :: _ if p == end => xs
59+ case p :: ys => go(p.neighbors.filter(canMove(ys)) ++ xs)
6160
6261 go(List (start)).reverseIterator.toVector
6362
6463 lazy val zipped = path.zipWithIndex
6564 lazy val pathMap = zipped.toMap
6665
67- def cheatedPaths (maxDistance : Int ) =
68- def radius (p : Pos ) = for
69- x <- ((p.x ± maxDistance) & bounds.widthRange).iterator
70- y <- ((p.y ± maxDistance) & bounds.heightRange).iterator
71- pos = Pos (x, y) if (pos taxiDist p) <= maxDistance
72- yield pos
66+ def cheatedPaths (maxDist : Int ) =
67+ def radius (p : Pos ) =
68+ (Rect (p.x ± maxDist, p.y ± maxDist) & bounds).iterator
69+ .filter(p.taxiDist(_) <= maxDist)
7370
7471 zipped.par.map { (p, i) =>
7572 radius(p)
0 commit comments