Skip to content

Commit f7525f8

Browse files
committed
refactor: simplify data types
1 parent 01235da commit f7525f8

File tree

1 file changed

+26
-29
lines changed

1 file changed

+26
-29
lines changed

2024/day20.scala

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import scala.collection.parallel.CollectionConverters.*
55
import scala.collection.immutable.Range.Inclusive
66
import prelude.time
77

8-
extension [A](x: A) inline def tap[B](f: A => B): A = { f(x); x }
98
extension (x: Int) inline def ±(y: Int) = x - y to x + y
109
extension (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

4038
object 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

Comments
 (0)