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