@@ -35,8 +35,10 @@ pub fn parse(input: &str) -> Input<'_> {
35
35
pub fn part1 ( input : & Input < ' _ > ) -> i32 {
36
36
let ( grid, moves) = input;
37
37
38
+ // We don't need to move the robot symbol so mark as empty space once located.
38
39
let mut grid = grid. clone ( ) ;
39
40
let mut position = grid. find ( b'@' ) . unwrap ( ) ;
41
+ grid[ position] = b'.' ;
40
42
41
43
// Treat moves as a single string ignoring any newline characters.
42
44
for b in moves. bytes ( ) {
@@ -57,18 +59,17 @@ pub fn part2(input: &Input<'_>) -> i32 {
57
59
58
60
let mut grid = stretch ( grid) ;
59
61
let mut position = grid. find ( b'@' ) . unwrap ( ) ;
62
+ grid[ position] = b'.' ;
60
63
61
64
// Reuse to minimize allocations.
62
- let mut todo = Vec :: new ( ) ;
63
- let mut seen = grid. same_size_with ( usize:: MAX ) ;
65
+ let mut todo = Vec :: with_capacity ( 50 ) ;
64
66
65
- // Use index as a unique id for each move.
66
- for ( id, b) in moves. bytes ( ) . enumerate ( ) {
67
+ for b in moves. bytes ( ) {
67
68
match b {
68
69
b'<' => narrow ( & mut grid, & mut position, LEFT ) ,
69
70
b'>' => narrow ( & mut grid, & mut position, RIGHT ) ,
70
- b'^' => wide ( & mut grid, & mut position, UP , & mut todo, & mut seen , id ) ,
71
- b'v' => wide ( & mut grid, & mut position, DOWN , & mut todo, & mut seen , id ) ,
71
+ b'^' => wide ( & mut grid, & mut position, UP , & mut todo) ,
72
+ b'v' => wide ( & mut grid, & mut position, DOWN , & mut todo) ,
72
73
_ => ( ) ,
73
74
}
74
75
}
@@ -78,7 +79,7 @@ pub fn part2(input: &Input<'_>) -> i32 {
78
79
79
80
fn narrow ( grid : & mut Grid < u8 > , start : & mut Point , direction : Point ) {
80
81
let mut position = * start + direction;
81
- let mut size = 2 ;
82
+ let mut size = 1 ;
82
83
83
84
// Search for the next wall or open space.
84
85
while grid[ position] != b'.' && grid[ position] != b'#' {
@@ -89,7 +90,7 @@ fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
89
90
// Move items one space in direction.
90
91
if grid[ position] == b'.' {
91
92
let mut previous = b'.' ;
92
- let mut position = * start;
93
+ let mut position = * start + direction ;
93
94
94
95
for _ in 0 ..size {
95
96
swap ( & mut previous, & mut grid[ position] ) ;
@@ -101,58 +102,42 @@ fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
101
102
}
102
103
}
103
104
104
- fn wide (
105
- grid : & mut Grid < u8 > ,
106
- start : & mut Point ,
107
- direction : Point ,
108
- todo : & mut Vec < Point > ,
109
- seen : & mut Grid < usize > ,
110
- id : usize ,
111
- ) {
105
+ fn wide ( grid : & mut Grid < u8 > , start : & mut Point , direction : Point , todo : & mut Vec < Point > ) {
112
106
// Short circuit if path in front of robot is empty.
113
- let position = * start;
114
- let next = position + direction;
115
-
116
- if grid[ next] == b'.' {
117
- grid[ position] = b'.' ;
118
- grid[ next] = b'@' ;
107
+ if grid[ * start + direction] == b'.' {
119
108
* start += direction;
120
109
return ;
121
110
}
122
111
123
112
// Clear any items from previous push.
124
113
todo. clear ( ) ;
114
+ // Add dummy item at the start to prevent index of out bounds when checking for previously
115
+ // added boxes.
116
+ todo. push ( ORIGIN ) ;
125
117
todo. push ( * start) ;
126
- let mut index = 0 ;
118
+ let mut index = 1 ;
127
119
128
120
while index < todo. len ( ) {
129
121
let next = todo[ index] + direction;
130
122
index += 1 ;
131
123
132
- let other = match grid[ next] {
124
+ // Add boxes strictly left to right.
125
+ let ( first, second) = match grid[ next] {
126
+ b'[' => ( next, next + RIGHT ) ,
127
+ b']' => ( next + LEFT , next) ,
133
128
b'#' => return , // Return early if there's a wall in the way.
134
- b'[' => RIGHT ,
135
- b']' => LEFT ,
136
- _ => continue , // Open space doesn't add any more items to move.
129
+ _ => continue , // Open space doesn't add any more items to move.
137
130
} ;
138
131
139
- // Enqueue the first half of box directly above us.
140
- let first = next;
141
- if seen[ first] != id {
142
- seen[ first] = id;
132
+ // Check if this box has already been added by the previous box in this row.
133
+ if first != todo[ todo. len ( ) - 2 ] {
143
134
todo. push ( first) ;
144
- }
145
-
146
- // Enqueue the other half of the box directly above us.
147
- let second = next + other;
148
- if seen[ second] != id {
149
- seen[ second] = id;
150
135
todo. push ( second) ;
151
136
}
152
137
}
153
138
154
- // Move boxes in reverse order.
155
- for & point in todo. iter ( ) . rev ( ) {
139
+ // Move boxes in reverse order (skipping the robot as an optimization) .
140
+ for & point in todo[ 2 .. ] . iter ( ) . rev ( ) {
156
141
grid[ point + direction] = grid[ point] ;
157
142
grid[ point] = b'.' ;
158
143
}
0 commit comments