1
+ //! # Unstable Diffusion
2
+ //!
3
+ //! We represent elves as bits in a integer then use bitwise operations to efficiently figure
4
+ //! out the movement for multiple elves at once.
1
5
use self :: Direction :: * ;
2
6
use std:: ops:: { BitAnd , BitAndAssign , BitOr , Not } ;
3
7
8
+ /// The initial grid is 70 x 70. Elves stop moving when no other elf is adjacent so the grid
9
+ /// will expand at most 70 in any direction, giving 70 + 70 + 70 = 210 total.
4
10
const HEIGHT : usize = 210 ;
5
11
12
+ /// Duct tape two `u128`s together.
6
13
#[ derive( Clone , Copy , Default ) ]
7
14
pub struct U256 {
8
15
left : u128 ,
@@ -26,6 +33,7 @@ impl U256 {
26
33
self . left != 0 || self . right != 0
27
34
}
28
35
36
+ /// Used to find the bounding rectangle for part one.
29
37
fn min_set ( & self ) -> Option < u32 > {
30
38
if self . left != 0 {
31
39
Some ( self . left . leading_zeros ( ) )
@@ -36,6 +44,7 @@ impl U256 {
36
44
}
37
45
}
38
46
47
+ /// Used to find the bounding rectangle for part one.
39
48
fn max_set ( & self ) -> Option < u32 > {
40
49
if self . right != 0 {
41
50
Some ( 255 - self . right . trailing_zeros ( ) )
@@ -55,6 +64,7 @@ impl U256 {
55
64
}
56
65
}
57
66
67
+ /// Syntactic sugar to provide the regular `&`, `|` and `!` bitwise operator notation.
58
68
impl BitAnd for U256 {
59
69
type Output = U256 ;
60
70
@@ -102,7 +112,9 @@ pub struct Input {
102
112
east : [ U256 ; HEIGHT ] ,
103
113
}
104
114
115
+ /// Converts the ASCII grid into a bit per elf.
105
116
pub fn parse ( input : & str ) -> Input {
117
+ // Enough buffer so that elves won't overflow the edges of the grid.
106
118
let offset = 70 ;
107
119
let raw: Vec < _ > = input. lines ( ) . map ( str:: as_bytes) . collect ( ) ;
108
120
let default = [ U256 :: default ( ) ; HEIGHT ] ;
@@ -127,6 +139,7 @@ pub fn part1(input: &Input) -> u32 {
127
139
step ( & mut input, & mut order) ;
128
140
}
129
141
142
+ // Find the bounding rectangle.
130
143
let grid = input. grid ;
131
144
let elves: u32 = grid. iter ( ) . map ( U256 :: count_ones) . sum ( ) ;
132
145
let min_x = grid. iter ( ) . filter_map ( U256 :: min_set) . min ( ) . unwrap ( ) ;
@@ -153,27 +166,34 @@ pub fn part2(input: &Input) -> u32 {
153
166
154
167
fn step ( input : & mut Input , order : & mut [ Direction ] ) -> bool {
155
168
let Input { grid, north, south, west, east } = input;
169
+ // Optimization to avoid processing empty rows.
156
170
let start = grid. iter ( ) . position ( U256 :: non_zero) . unwrap ( ) - 1 ;
157
171
let end = grid. iter ( ) . rposition ( U256 :: non_zero) . unwrap ( ) + 2 ;
158
172
159
173
let mut moved = false ;
160
174
161
175
let mut prev;
176
+ // Find horizontal neighbors in each row. To make movement calculations easier
177
+ // we invert so that a bit is 1 is movement is *possible*.
162
178
let mut cur = !( grid[ 0 ] . right_shift ( ) | grid[ 0 ] | grid[ 0 ] . left_shift ( ) ) ;
163
179
let mut next = !( grid[ 1 ] . right_shift ( ) | grid[ 1 ] | grid[ 1 ] . left_shift ( ) ) ;
164
180
165
181
for i in start..end {
182
+ // Calculating neighbors is relatively expensive so re-use results between rows.
166
183
prev = cur;
167
184
cur = next;
168
185
next = !( grid[ i + 1 ] . right_shift ( ) | grid[ i + 1 ] | grid[ i + 1 ] . left_shift ( ) ) ;
169
186
170
187
let mut up = prev;
171
188
let mut down = next;
172
- let horizontal = !( grid[ i - 1 ] | grid[ i] | grid[ i + 1 ] ) ;
173
- let mut left = horizontal. right_shift ( ) ;
174
- let mut right = horizontal. left_shift ( ) ;
189
+ // Find neighours in vertical columns.
190
+ let vertical = !( grid[ i - 1 ] | grid[ i] | grid[ i + 1 ] ) ;
191
+ let mut left = vertical. right_shift ( ) ;
192
+ let mut right = vertical. left_shift ( ) ;
193
+ // Elves need at least 1 neighbor to propose moving.
175
194
let mut remaining = grid[ i] & !( up & down & left & right) ;
176
195
196
+ // Consider each direction one at a time, removing any elves who propose it.
177
197
for direction in & * order {
178
198
match direction {
179
199
North => {
@@ -195,12 +215,16 @@ fn step(input: &mut Input, order: &mut [Direction]) -> bool {
195
215
}
196
216
}
197
217
218
+ // Copy final proposals to an array for each direction.
198
219
north[ i - 1 ] = up;
199
220
south[ i + 1 ] = down;
200
221
west[ i] = left. left_shift ( ) ;
201
222
east[ i] = right. right_shift ( ) ;
202
223
}
203
224
225
+ // Elves that propose moving to the same spot cancel each other out and no-one moves.
226
+ // Due to the movement rules we only need to check horizontal and vertical movement into
227
+ // the same spot (horizontal and vertical movement can never collide with each other).
204
228
for i in start..end {
205
229
let up = north[ i] ;
206
230
let down = south[ i] ;
@@ -213,13 +237,16 @@ fn step(input: &mut Input, order: &mut [Direction]) -> bool {
213
237
}
214
238
215
239
for i in start..end {
240
+ // Stationary elves.
216
241
let same =
217
242
grid[ i] & !( north[ i - 1 ] | south[ i + 1 ] | west[ i] . right_shift ( ) | east[ i] . left_shift ( ) ) ;
243
+ // Moving elves.
218
244
let change = north[ i] | south[ i] | west[ i] | east[ i] ;
219
245
grid[ i] = same | change;
220
246
moved |= change. non_zero ( ) ;
221
247
}
222
248
249
+ // Rotate the order of movement proposals for the next turn.
223
250
order. rotate_left ( 1 ) ;
224
251
moved
225
252
}
0 commit comments