@@ -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,18 @@ 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
67
// Use index as a unique id for each move.
66
- for ( id , b ) in moves. bytes ( ) . enumerate ( ) {
68
+ for b in moves. bytes ( ) {
67
69
match b {
68
70
b'<' => narrow ( & mut grid, & mut position, LEFT ) ,
69
71
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 ) ,
72
+ b'^' => wide ( & mut grid, & mut position, UP , & mut todo) ,
73
+ b'v' => wide ( & mut grid, & mut position, DOWN , & mut todo) ,
72
74
_ => ( ) ,
73
75
}
74
76
}
@@ -78,7 +80,7 @@ pub fn part2(input: &Input<'_>) -> i32 {
78
80
79
81
fn narrow ( grid : & mut Grid < u8 > , start : & mut Point , direction : Point ) {
80
82
let mut position = * start + direction;
81
- let mut size = 2 ;
83
+ let mut size = 1 ;
82
84
83
85
// Search for the next wall or open space.
84
86
while grid[ position] != b'.' && grid[ position] != b'#' {
@@ -89,7 +91,7 @@ fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
89
91
// Move items one space in direction.
90
92
if grid[ position] == b'.' {
91
93
let mut previous = b'.' ;
92
- let mut position = * start;
94
+ let mut position = * start + direction ;
93
95
94
96
for _ in 0 ..size {
95
97
swap ( & mut previous, & mut grid[ position] ) ;
@@ -101,58 +103,42 @@ fn narrow(grid: &mut Grid<u8>, start: &mut Point, direction: Point) {
101
103
}
102
104
}
103
105
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
- ) {
106
+ fn wide ( grid : & mut Grid < u8 > , start : & mut Point , direction : Point , todo : & mut Vec < Point > ) {
112
107
// 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'@' ;
108
+ if grid[ * start + direction] == b'.' {
119
109
* start += direction;
120
110
return ;
121
111
}
122
112
123
113
// Clear any items from previous push.
124
114
todo. clear ( ) ;
115
+ // Add dummy item at the start to prevent index of out bounds when checking for previously
116
+ // added boxes.
117
+ todo. push ( ORIGIN ) ;
125
118
todo. push ( * start) ;
126
- let mut index = 0 ;
119
+ let mut index = 1 ;
127
120
128
121
while index < todo. len ( ) {
129
122
let next = todo[ index] + direction;
130
123
index += 1 ;
131
124
132
- let other = match grid[ next] {
125
+ // Add boxes strictly left to right.
126
+ let ( first, second) = match grid[ next] {
127
+ b'[' => ( next, next + RIGHT ) ,
128
+ b']' => ( next + LEFT , next) ,
133
129
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.
130
+ _ => continue , // Open space doesn't add any more items to move.
137
131
} ;
138
132
139
- // Enqueue the first half of box directly above us.
140
- let first = next;
141
- if seen[ first] != id {
142
- seen[ first] = id;
133
+ // Check if this box has already been added by the previous box in this row.
134
+ if first != todo[ todo. len ( ) - 2 ] {
143
135
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
136
todo. push ( second) ;
151
137
}
152
138
}
153
139
154
- // Move boxes in reverse order.
155
- for & point in todo. iter ( ) . rev ( ) {
140
+ // Move boxes in reverse order (skipping the robot as an optimization) .
141
+ for & point in todo[ 2 .. ] . iter ( ) . rev ( ) {
156
142
grid[ point + direction] = grid[ point] ;
157
143
grid[ point] = b'.' ;
158
144
}
0 commit comments