@@ -7,21 +7,18 @@ use nd_vec::Vec2;
7
7
solution ! ( "Hoof It" , 10 ) ;
8
8
9
9
fn part_a ( input : & str ) -> Answer {
10
- let map = Map :: parse ( input) ;
11
- map. trailheads ( )
12
- . into_iter ( )
13
- . map ( |x| map. score ( x) )
14
- . sum :: < usize > ( )
15
- . into ( )
10
+ solve ( input, false ) . into ( )
16
11
}
17
12
18
13
fn part_b ( input : & str ) -> Answer {
14
+ solve ( input, true ) . into ( )
15
+ }
16
+
17
+ fn solve ( input : & str , part_b : bool ) -> usize {
19
18
let map = Map :: parse ( input) ;
20
19
map. trailheads ( )
21
- . into_iter ( )
22
- . map ( |x| map. rating ( x) )
20
+ . map ( |x| map. score ( x, !part_b) )
23
21
. sum :: < usize > ( )
24
- . into ( )
25
22
}
26
23
27
24
struct Map {
@@ -34,15 +31,17 @@ impl Map {
34
31
Self { board }
35
32
}
36
33
37
- fn trailheads ( & self ) -> Vec < Vec2 < usize > > {
34
+ // Find the coordinates of all 0s
35
+ fn trailheads ( & self ) -> impl Iterator < Item = Vec2 < usize > > + use < ' _ > {
38
36
self . board
39
37
. iter ( )
40
38
. filter ( |( _, & tile) | tile == 0 )
41
39
. map ( |( pos, _) | pos)
42
- . collect ( )
43
40
}
44
41
45
- fn score ( & self , pos : Vec2 < usize > ) -> usize {
42
+ // Simple BFS for pathfinding, where we don't avoid going to already
43
+ // explored tiles if on part B.
44
+ fn score ( & self , pos : Vec2 < usize > , no_repeats : bool ) -> usize {
46
45
let mut queue = VecDeque :: new ( ) ;
47
46
let mut seen = HashSet :: new ( ) ;
48
47
@@ -52,49 +51,22 @@ impl Map {
52
51
let mut score = 0 ;
53
52
while let Some ( pos) = queue. pop_front ( ) {
54
53
let value = * self . board . get ( pos) . unwrap ( ) ;
55
- if value == 9 {
56
- score += 1 ;
57
- }
58
-
59
- for dir in Direction :: ALL {
60
- if let Some ( next) = dir. try_advance ( pos) {
61
- if self . board . contains ( next)
62
- && * self . board . get ( next) . unwrap ( ) == value + 1
63
- && seen. insert ( next)
64
- {
65
- queue. push_back ( next) ;
66
- }
67
- }
68
- }
54
+ score += ( value == 9 ) as usize ;
55
+
56
+ queue. extend (
57
+ Direction :: ALL
58
+ . iter ( )
59
+ . filter_map ( |& dir| dir. try_advance ( pos) )
60
+ . filter ( |& next| {
61
+ self . board . contains ( next)
62
+ && * self . board . get ( next) . unwrap ( ) == value + 1
63
+ && ( !no_repeats || seen. insert ( next) )
64
+ } ) ,
65
+ ) ;
69
66
}
70
67
71
68
score
72
69
}
73
-
74
- fn rating ( & self , pos : Vec2 < usize > ) -> usize {
75
- fn inner ( board : & Matrix < u32 > , pos : Vec2 < usize > , mut seen : HashSet < Vec2 < usize > > ) -> usize {
76
- let value = * board. get ( pos) . unwrap ( ) ;
77
- if value == 9 {
78
- return 1 ;
79
- }
80
-
81
- let mut sum = 0 ;
82
- for dir in Direction :: ALL {
83
- if let Some ( next) = dir. try_advance ( pos) {
84
- if board. contains ( next)
85
- && * board. get ( next) . unwrap ( ) == value + 1
86
- && seen. insert ( next)
87
- {
88
- sum += inner ( board, next, seen. clone ( ) ) ;
89
- }
90
- }
91
- }
92
-
93
- sum
94
- }
95
-
96
- inner ( & self . board , pos, HashSet :: new ( ) )
97
- }
98
70
}
99
71
100
72
#[ cfg( test) ]
0 commit comments