1use Direction::*;
6use implementation::U256;
7
8const HEIGHT: usize = 210;
11
12enum Direction {
13 North,
14 South,
15 West,
16 East,
17}
18
19#[derive(Clone, Copy)]
20pub struct Input {
21 grid: [U256; HEIGHT],
22 north: [U256; HEIGHT],
23 south: [U256; HEIGHT],
24 west: [U256; HEIGHT],
25 east: [U256; HEIGHT],
26}
27
28pub fn parse(input: &str) -> Input {
30 let offset = 70;
32 let default = [U256::default(); HEIGHT];
33 let mut grid = default;
34
35 for (y, row) in input.lines().enumerate() {
36 for (x, col) in row.bytes().enumerate() {
37 if col == b'#' {
38 grid[offset + y].set_bit(offset + x);
39 }
40 }
41 }
42
43 Input { grid, north: default, south: default, west: default, east: default }
44}
45
46pub fn part1(input: &Input) -> usize {
47 let mut input = *input;
48 let mut order = [North, South, West, East];
49
50 for _ in 0..10 {
51 step(&mut input, &mut order);
52 }
53
54 let grid = input.grid;
56 let elves = grid.iter().flat_map(U256::as_array).map(u8::count_ones).sum::<u32>() as usize;
57
58 let min_y = grid.iter().position(U256::non_zero).unwrap();
60 let max_y = grid.iter().rposition(U256::non_zero).unwrap();
61
62 let array = grid.iter().fold(U256::default(), |acc, &n| acc.or(n)).as_array();
64 let left = array.iter().position(|&e| e != 0).unwrap();
65 let right = array.iter().rposition(|&e| e != 0).unwrap();
66
67 let min_x = 8 * left + array[left].leading_zeros() as usize;
68 let max_x = 8 * right + (7 - array[right].trailing_zeros()) as usize;
69
70 (max_x - min_x + 1) * (max_y - min_y + 1) - elves
72}
73
74pub fn part2(input: &Input) -> u32 {
75 let mut input = *input;
76 let mut order = [North, South, West, East];
77 let mut count = 1;
78
79 while step(&mut input, &mut order) {
80 count += 1;
81 }
82
83 count
84}
85
86fn step(input: &mut Input, order: &mut [Direction]) -> bool {
87 let Input { grid, north, south, west, east } = input;
88 let start = grid.iter().position(U256::non_zero).unwrap() - 1;
90 let end = grid.iter().rposition(U256::non_zero).unwrap() + 2;
91
92 let mut moved = false;
93
94 let mut prev;
95 let mut cur = grid[0].shr().or(grid[0]).or(grid[0].shl()).not();
98 let mut next = grid[1].shr().or(grid[1]).or(grid[1].shl()).not();
99
100 for i in start..end {
101 prev = cur;
103 cur = next;
104 next = grid[i + 1].shr().or(grid[i + 1]).or(grid[i + 1].shl()).not();
105
106 let mut up = prev;
107 let mut down = next;
108 let vertical = grid[i - 1].or(grid[i]).or(grid[i + 1]).not();
110 let mut left = vertical.shr();
111 let mut right = vertical.shl();
112 let mut remaining = grid[i].and(up.and(down).and(left).and(right).not());
114
115 for direction in &*order {
117 match direction {
118 North => {
119 up = up.and(remaining);
120 remaining = remaining.and(up.not());
121 }
122 South => {
123 down = down.and(remaining);
124 remaining = remaining.and(down.not());
125 }
126 West => {
127 left = left.and(remaining);
128 remaining = remaining.and(left.not());
129 }
130 East => {
131 right = right.and(remaining);
132 remaining = remaining.and(right.not());
133 }
134 }
135 }
136
137 north[i - 1] = up;
139 south[i + 1] = down;
140 west[i] = left.shl();
141 east[i] = right.shr();
142 }
143
144 for i in start..end {
148 let up = north[i];
149 let down = south[i];
150 let left = west[i];
151 let right = east[i];
152 north[i] = north[i].and(down.not());
153 south[i] = south[i].and(up.not());
154 west[i] = west[i].and(right.not());
155 east[i] = east[i].and(left.not());
156 }
157
158 for i in start..end {
159 let same =
161 grid[i].and(north[i - 1].or(south[i + 1]).or(west[i].shr()).or(east[i].shl()).not());
162 let change = north[i].or(south[i]).or(west[i]).or(east[i]);
164 grid[i] = same.or(change);
165 moved |= change.non_zero();
166 }
167
168 order.rotate_left(1);
170 moved
171}
172
173#[cfg(not(feature = "simd"))]
174mod implementation {
175 #[derive(Clone, Copy, Default)]
177 pub(super) struct U256 {
178 left: u128,
179 right: u128,
180 }
181
182 impl U256 {
183 pub(super) fn set_bit(&mut self, offset: usize) {
184 if offset < 128 {
185 self.left |= 1 << (127 - offset);
186 } else {
187 self.right |= 1 << (255 - offset);
188 }
189 }
190
191 pub(super) fn as_array(&self) -> [u8; 32] {
192 let mut result = [0; 32];
193 result[..16].copy_from_slice(&self.left.to_be_bytes());
194 result[16..].copy_from_slice(&self.right.to_be_bytes());
195 result
196 }
197
198 pub(super) fn non_zero(&self) -> bool {
199 self.left != 0 || self.right != 0
200 }
201
202 pub(super) fn shl(self) -> U256 {
203 U256 { left: (self.left << 1) | (self.right >> 127), right: (self.right << 1) }
204 }
205
206 pub(super) fn shr(self) -> U256 {
207 U256 { left: (self.left >> 1), right: (self.left << 127) | (self.right >> 1) }
208 }
209
210 pub(super) fn and(self, rhs: U256) -> U256 {
211 U256 { left: self.left & rhs.left, right: self.right & rhs.right }
212 }
213
214 pub(super) fn or(self, rhs: U256) -> U256 {
215 U256 { left: self.left | rhs.left, right: self.right | rhs.right }
216 }
217
218 pub(super) fn not(self) -> U256 {
219 U256 { left: !self.left, right: !self.right }
220 }
221 }
222}
223
224#[cfg(feature = "simd")]
225mod implementation {
226 use std::simd::*;
227
228 #[derive(Clone, Copy, Default)]
229 pub(super) struct U256 {
230 v: Simd<u8, 32>,
231 }
232
233 impl U256 {
234 pub(super) fn set_bit(&mut self, offset: usize) {
235 self.v[offset / 8] |= 1 << (7 - offset % 8);
236 }
237
238 pub(super) fn as_array(&self) -> [u8; 32] {
239 self.v.to_array()
240 }
241
242 pub(super) fn non_zero(&self) -> bool {
243 self.v != Simd::splat(0)
244 }
245
246 pub(super) fn shl(self) -> Self {
247 Self { v: (self.v << 1) | (self.v.shift_elements_left::<1>(0) >> 7) }
248 }
249
250 pub(super) fn shr(self) -> Self {
251 Self { v: (self.v >> 1) | (self.v.shift_elements_right::<1>(0) << 7) }
252 }
253
254 pub(super) fn and(self, rhs: Self) -> Self {
255 Self { v: self.v & rhs.v }
256 }
257
258 pub(super) fn or(self, rhs: Self) -> Self {
259 Self { v: self.v | rhs.v }
260 }
261
262 pub(super) fn not(self) -> Self {
263 Self { v: !self.v }
264 }
265 }
266}