@@ -7,12 +7,12 @@ import locations.Directory.currentDir
7
7
import inputs .Input .loadFileSync
8
8
9
9
@ main def part1 : Unit =
10
- val ans = longestDownhillHike(start, 0 )
11
- println(s " The solution is $ans " )
10
+ given maze : Maze = Maze (loadInput() )
11
+ println(s " The solution is $longestDownhillHike " )
12
12
13
13
@ main def part2 : Unit =
14
- val ans = longestHike(indexOf(start), BitSet .empty, 0 )
15
- println(s " The solution is $ans " )
14
+ given maze : Maze = Maze (loadInput() )
15
+ println(s " The solution is $longestHike " )
16
16
17
17
def loadInput (): Vector [Vector [Char ]] = Vector .from:
18
18
val file = loadFileSync(s " $currentDir/../input/day23 " )
@@ -44,68 +44,78 @@ case class Point(x: Int, y: Int):
44
44
case Dir .E => copy(x = x + 1 )
45
45
case Dir .W => copy(x = x - 1 )
46
46
47
- val grid = loadInput()
47
+ case class Maze ( grid : Vector [ Vector [ Char ]]) :
48
48
49
- val xRange = grid.head.indices
50
- val yRange = grid.indices
49
+ def apply (p : Point ): Char = grid(p.y)(p.x)
51
50
52
- def points : Iterator [Point ] = for
53
- y <- yRange.iterator
54
- x <- xRange.iterator
55
- yield Point (x, y)
51
+ val xRange = grid.head.indices
52
+ val yRange = grid.indices
56
53
57
- val slopes = Map .from[Point , Dir ]:
58
- points.collect:
59
- case p if grid(p.y)(p.x) == '^' => p -> Dir .N
60
- case p if grid(p.y)(p.x) == 'v' => p -> Dir .S
61
- case p if grid(p.y)(p.x) == '>' => p -> Dir .E
62
- case p if grid(p.y)(p.x) == '<' => p -> Dir .W
54
+ def points : Iterator [Point ] = for
55
+ y <- yRange.iterator
56
+ x <- xRange.iterator
57
+ yield Point (x, y)
63
58
64
- val walkable = points.filter(p => grid(p.y)(p.x) != '#' ).toSet
65
- val start = walkable.minBy(_.y)
66
- val end = walkable.maxBy(_.y)
59
+ val walkable = points.filter(p => grid(p.y)(p.x) != '#' ).toSet
60
+ val start = walkable.minBy(_.y)
61
+ val end = walkable.maxBy(_.y)
67
62
68
- val nodes : Set [Point ] = walkable.filter: p =>
69
- Dir .values.map(p.move).count(walkable) > 2
70
- .toSet + start + end
63
+ val slopes = Map .from[Point , Dir ]:
64
+ points.collect:
65
+ case p if apply(p) == '^' => p -> Dir .N
66
+ case p if apply(p) == 'v' => p -> Dir .S
67
+ case p if apply(p) == '>' => p -> Dir .E
68
+ case p if apply(p) == '<' => p -> Dir .W
71
69
72
- def next (pos : Point , dir : Dir ): List [(Point , Dir )] =
70
+ val nodes : Set [Point ] = walkable.filter: p =>
71
+ Dir .values.map(p.move).count(walkable) > 2
72
+ .toSet + start + end
73
+
74
+
75
+ def next (pos : Point , dir : Dir )(using maze : Maze ): List [(Point , Dir )] =
73
76
for
74
77
d <- List (dir, dir.turnRight, dir.turnLeft)
75
78
p = pos.move(d)
76
- if slopes.get(p).forall(_ == d)
77
- if walkable(p)
79
+ if maze. slopes.get(p).forall(_ == d)
80
+ if maze. walkable(p)
78
81
yield p -> d
79
82
80
- def nodesFrom (pos : Point ) = List .from[(Point , Int )]:
83
+ def nodesFrom (pos : Point )( using maze : Maze ) = List .from[(Point , Int )]:
81
84
def search (p : Point , d : Dir , dist : Int ): Option [(Point , Int )] =
82
85
next(p, d) match
83
- case (p, d) :: Nil if nodes(p) => Some (p, dist + 1 )
86
+ case (p, d) :: Nil if maze. nodes(p) => Some (p, dist + 1 )
84
87
case (p, d) :: Nil => search(p, d, dist + 1 )
85
88
case _ => None
86
89
87
90
Dir .values.flatMap(next(pos, _)).distinct.flatMap(search(_, _, 1 ))
88
91
89
- def longestDownhillHike (pos : Point , dist : Int ): Int =
90
- if pos == end then dist else
91
- nodesFrom(pos).foldLeft(0 ):
92
- case (max, (n, d)) => max.max(longestDownhillHike(n, dist + d))
93
-
94
- type Node = Int
95
- val indexOf : Map [Point , Node ] =
96
- nodes.toList.sortBy(_.dist(start)).zipWithIndex.toMap
97
-
98
- val fullAdj : Map [Node , List [(Node , Int )]] =
99
- nodes.toList.flatMap: p1 =>
100
- nodesFrom(p1).flatMap: (p2, d) =>
101
- val forward = indexOf(p1) -> (indexOf(p2), d)
102
- val reverse = indexOf(p2) -> (indexOf(p1), d)
103
- List (forward, reverse)
104
- .groupMap(_._1)(_._2)
105
-
106
- def longestHike (node : Node , visited : BitSet , dist : Int ): Int =
107
- if node == indexOf(end) then dist else
108
- fullAdj(node).foldLeft(0 ):
109
- case (max, (n, d)) =>
110
- if visited(n) then max else
111
- max.max(longestHike(n, visited + n, dist + d))
92
+ def longestDownhillHike (using maze : Maze ): Int =
93
+ def search (pos : Point , dist : Int )(using maze : Maze ): Int =
94
+ if pos == maze.end then dist else
95
+ nodesFrom(pos).foldLeft(0 ):
96
+ case (max, (n, d)) => max.max(search(n, dist + d))
97
+
98
+ search(maze.start, 0 )
99
+
100
+ def longestHike (using maze : Maze ): Int =
101
+ type Index = Int
102
+
103
+ val indexOf : Map [Point , Index ] =
104
+ maze.nodes.toList.sortBy(_.dist(maze.start)).zipWithIndex.toMap
105
+
106
+ val adjacent : Map [Index , List [(Index , Int )]] =
107
+ maze.nodes.toList.flatMap: p1 =>
108
+ nodesFrom(p1).flatMap: (p2, d) =>
109
+ val forward = indexOf(p1) -> (indexOf(p2), d)
110
+ val reverse = indexOf(p2) -> (indexOf(p1), d)
111
+ List (forward, reverse)
112
+ .groupMap(_._1)(_._2)
113
+
114
+ def search (node : Index , visited : BitSet , dist : Int ): Int =
115
+ if node == indexOf(maze.end) then dist else
116
+ adjacent(node).foldLeft(0 ):
117
+ case (max, (n, d)) =>
118
+ if visited(n) then max else
119
+ max.max(search(n, visited + n, dist + d))
120
+
121
+ search(indexOf(maze.start), BitSet .empty, 0 )
0 commit comments