@@ -3,154 +3,136 @@ use crate::util::hash::*;
3
3
use crate :: util:: point:: * ;
4
4
use std:: collections:: VecDeque ;
5
5
6
- pub fn parse ( input : & str ) -> Grid < u8 > {
7
- Grid :: parse ( input)
6
+ pub struct Input {
7
+ directed : [ u64 ; 36 ] ,
8
+ undirected : [ u64 ; 36 ] ,
9
+ weight : [ [ u32 ; 36 ] ; 36 ] ,
8
10
}
9
11
10
- pub fn part1 ( grid : & Grid < u8 > ) -> i32 {
11
- let start = Point :: new ( 1 , 0 ) ;
12
- let end = Point :: new ( grid. width - 2 , grid. height - 1 ) ;
12
+ pub fn parse ( input : & str ) -> Input {
13
+ let mut grid = Grid :: parse ( input) ;
14
+ let width = grid. width ;
15
+ let height = grid. height ;
13
16
14
- let mut seen = FastSet :: new ( ) ;
15
- let mut todo = VecDeque :: new ( ) ;
16
- let mut result = 0 ;
17
-
18
- seen. insert ( start) ;
19
- todo. push_back ( ( 0 , start, seen) ) ;
20
-
21
- while let Some ( ( cost, pos, seen) ) = todo. pop_front ( ) {
22
- if pos == end {
23
- result = result. max ( cost) ;
24
- continue ;
25
- }
26
- if !grid. contains ( pos) {
27
- continue ;
28
- }
17
+ // Modify edge of grid to remove the need for boundary checks.
18
+ grid[ Point :: new ( 1 , 0 ) ] = b'#' ;
19
+ grid[ Point :: new ( width - 2 , height - 1 ) ] = b'#' ;
29
20
30
- let b = grid[ pos] ;
21
+ // Move start and end away from edge.
22
+ let start = Point :: new ( 1 , 1 ) ;
23
+ let end = Point :: new ( width - 2 , height - 2 ) ;
31
24
32
- if b == b'.' || b == b'v' {
33
- let next = pos + DOWN ;
34
- let mut copy = seen . clone ( ) ;
25
+ // Points of interest are start, end and junctions.
26
+ grid [ start ] = b'P' ;
27
+ grid [ end ] = b'P' ;
35
28
36
- if copy. insert ( next) {
37
- todo. push_back ( ( cost + 1 , next, copy) ) ;
38
- }
39
- }
40
-
41
- if b == b'.' || b == b'^' {
42
- let next = pos + UP ;
43
- let mut copy = seen. clone ( ) ;
44
-
45
- if copy. insert ( next) {
46
- todo. push_back ( ( cost + 1 , next, copy) ) ;
47
- }
48
- }
29
+ let mut poi = FastMap :: new ( ) ;
30
+ poi. insert ( start, 0 ) ;
31
+ poi. insert ( end, 1 ) ;
49
32
50
- if b == b'.' || b == b'>' {
51
- let next = pos + RIGHT ;
52
- let mut copy = seen . clone ( ) ;
33
+ for y in 1 ..height - 1 {
34
+ for x in 1 ..width - 1 {
35
+ let position = Point :: new ( x , y ) ;
53
36
54
- if copy. insert ( next) {
55
- todo. push_back ( ( cost + 1 , next, copy) ) ;
56
- }
57
- }
58
-
59
- if b == b'.' || b == b'<' {
60
- let next = pos + LEFT ;
61
- let mut copy = seen. clone ( ) ;
62
-
63
- if copy. insert ( next) {
64
- todo. push_back ( ( cost + 1 , next, copy) ) ;
37
+ if grid[ position] != b'#' {
38
+ let neighbors =
39
+ ORTHOGONAL . iter ( ) . map ( |& o| position + o) . filter ( |& n| grid[ n] != b'#' ) . count ( ) ;
40
+ if neighbors > 2 {
41
+ grid[ position] = b'P' ;
42
+ poi. insert ( position, poi. len ( ) ) ;
43
+ }
65
44
}
66
45
}
67
46
}
68
47
69
- result
70
- }
71
-
72
- pub fn part2 ( grid : & Grid < u8 > ) -> i32 {
73
- let start = Point :: new ( 1 , 0 ) ;
74
- let end = Point :: new ( grid. width - 2 , grid. height - 1 ) ;
75
-
76
- let mut poi = FastSet :: new ( ) ;
77
- poi. insert ( start) ;
78
- poi. insert ( end) ;
79
-
80
- for y in 0 ..grid. height {
81
- for x in 0 ..grid. width {
82
- let p = Point :: new ( x, y) ;
83
- if grid[ p] != b'#' {
84
- let mut neighbors = 0 ;
85
-
86
- for o in ORTHOGONAL {
87
- let next = p + o;
88
- if grid. contains ( next) && grid[ next] != b'#' {
89
- neighbors += 1 ;
48
+ // BFS to find distances between POIs.
49
+ let mut todo = VecDeque :: new ( ) ;
50
+ let mut directed = [ 0 ; 36 ] ;
51
+ let mut undirected = [ 0 ; 36 ] ;
52
+ let mut weight = [ [ 0 ; 36 ] ; 36 ] ;
53
+
54
+ for ( & start, & from) in & poi {
55
+ todo. push_back ( ( start, 0 , true ) ) ;
56
+ grid[ start] = b'#' ;
57
+
58
+ while let Some ( ( position, cost, forward) ) = todo. pop_front ( ) {
59
+ for direction in ORTHOGONAL {
60
+ let next = position + direction;
61
+
62
+ match grid[ next] {
63
+ b'#' => ( ) ,
64
+ b'P' => {
65
+ let to = poi[ & next] ;
66
+
67
+ if forward {
68
+ directed[ from] |= 1 << to;
69
+ } else {
70
+ directed[ to] |= 1 << from;
71
+ }
72
+
73
+ undirected[ from] |= 1 << to;
74
+ undirected[ to] |= 1 << from;
75
+
76
+ weight[ from] [ to] = cost + 1 ;
77
+ weight[ to] [ from] = cost + 1 ;
78
+ }
79
+ b'.' => {
80
+ todo. push_back ( ( next, cost + 1 , forward) ) ;
81
+ grid[ next] = b'#' ;
82
+ }
83
+ _ => {
84
+ let same = direction == Point :: from ( grid[ next] ) ;
85
+ todo. push_back ( ( next, cost + 1 , forward && same) ) ;
86
+ grid[ next] = b'#' ;
90
87
}
91
- }
92
-
93
- if neighbors > 2 {
94
- poi. insert ( p) ;
95
88
}
96
89
}
97
90
}
98
91
}
99
92
100
- let mut edges = FastMap :: new ( ) ;
101
-
102
- for & start in & poi {
103
- edges. insert ( start, bfs ( grid, & poi, start) ) ;
104
- }
105
-
106
- let mut result = 0 ;
93
+ Input { directed, undirected, weight }
94
+ }
107
95
108
- let mut seen = FastSet :: new ( ) ;
109
- seen . insert ( start ) ;
96
+ pub fn part1 ( input : & Input ) -> u32 {
97
+ let mut cost = [ 0 ; 36 ] ;
110
98
111
99
let mut todo = VecDeque :: new ( ) ;
112
- todo. push_back ( ( start , seen , 0 ) ) ;
100
+ todo. push_back ( 0 ) ;
113
101
114
- while let Some ( ( pos, seen, cost) ) = todo. pop_front ( ) {
115
- if pos == end {
116
- result = result. max ( cost) ;
117
- continue ;
118
- }
102
+ while let Some ( from) = todo. pop_front ( ) {
103
+ let mut nodes = input. directed [ from] ;
119
104
120
- for & ( next , extra ) in & edges [ & pos ] {
121
- if !seen . contains ( & next ) {
122
- let mut copy = seen . clone ( ) ;
123
- copy . insert ( next ) ;
105
+ while nodes > 0 {
106
+ let to = nodes . trailing_zeros ( ) as usize ;
107
+ let mask = 1 << to ;
108
+ nodes ^= mask ;
124
109
125
- todo . push_back ( ( next , copy , cost + extra ) ) ;
126
- }
110
+ cost [ to ] = cost [ to ] . max ( cost[ from ] + input . weight [ from ] [ to ] ) ;
111
+ todo . push_back ( to ) ;
127
112
}
128
113
}
129
114
130
- result
115
+ 2 + cost [ 1 ]
131
116
}
132
117
133
- fn bfs ( grid : & Grid < u8 > , poi : & FastSet < Point > , start : Point ) -> Vec < ( Point , i32 ) > {
134
- let mut todo = VecDeque :: new ( ) ;
135
- let mut seen = FastSet :: new ( ) ;
136
- let mut result = Vec :: new ( ) ;
118
+ pub fn part2 ( input : & Input ) -> u32 {
119
+ 2 + dfs ( input, 0 , 1 , 0 )
120
+ }
137
121
138
- todo. push_back ( ( start, 0 ) ) ;
139
- seen. insert ( start) ;
122
+ fn dfs ( input : & Input , from : usize , seen : u64 , cost : u32 ) -> u32 {
123
+ if from == 1 {
124
+ return cost;
125
+ }
140
126
141
- while let Some ( ( pos, cost) ) = todo. pop_front ( ) {
142
- if pos != start && poi. contains ( & pos) {
143
- result. push ( ( pos, cost) ) ;
144
- continue ;
145
- }
127
+ let mut nodes = input. undirected [ from] & !seen;
128
+ let mut result = 0 ;
146
129
147
- for o in ORTHOGONAL {
148
- let next = pos + o;
130
+ while nodes > 0 {
131
+ let to = nodes. trailing_zeros ( ) as usize ;
132
+ let mask = 1 << to;
133
+ nodes ^= mask;
149
134
150
- if grid. contains ( next) && grid[ next] != b'#' && seen. insert ( next) {
151
- todo. push_back ( ( next, cost + 1 ) ) ;
152
- }
153
- }
135
+ result = result. max ( dfs ( input, to, seen | mask, cost + input. weight [ from] [ to] ) ) ;
154
136
}
155
137
156
138
result
0 commit comments