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