1use crate::util::grid::*;
12use crate::util::point::*;
13use implementation::*;
14
15const SEAT: u8 = b'L';
16
17pub fn parse(input: &str) -> Grid<u8> {
18 Grid::parse(input)
19}
20
21pub fn part1(input: &Grid<u8>) -> u32 {
22 simulate(input, false, 4)
23}
24
25pub fn part2(input: &Grid<u8>) -> u32 {
26 simulate(input, true, 5)
27}
28
29#[cfg(not(feature = "simd"))]
30mod implementation {
31 use super::*;
32
33 struct Seat {
34 point: Point,
35 size: usize,
36 neighbors: [Point; 8],
37 }
38
39 pub(super) fn simulate(input: &Grid<u8>, part_two: bool, limit: u8) -> u32 {
40 let mut seats = Vec::new();
41
42 for y in 0..input.height {
43 for x in 0..input.width {
44 let point = Point::new(x, y);
45 if input[point] != SEAT {
46 continue;
47 }
48
49 let mut size = 0;
50 let mut neighbors = [ORIGIN; 8];
51
52 for direction in DIAGONAL {
53 let mut next = point + direction;
54
55 while part_two && input.contains(next) && input[next] != SEAT {
58 next += direction;
59 }
60
61 if input.contains(next) && input[next] == SEAT {
62 neighbors[size] = next;
63 size += 1;
64 }
65 }
66
67 seats.push(Seat { point, size, neighbors });
68 }
69 }
70
71 let mut current = input.same_size_with(0);
72 let mut next = input.same_size_with(0);
73
74 loop {
75 for seat in &seats {
76 let total: u8 = seat.neighbors[..seat.size].iter().map(|&i| current[i]).sum();
77
78 next[seat.point] = if current[seat.point] == 0 {
79 u8::from(total == 0)
80 } else {
81 u8::from(total < limit)
82 };
83 }
84
85 (current, next) = (next, current);
86 if current == next {
87 return current.bytes.iter().map(|&n| n as u32).sum();
88 }
89 }
90 }
91}
92
93#[cfg(feature = "simd")]
94mod implementation {
95 use super::*;
96 use std::simd::cmp::SimdPartialEq as _;
97 use std::simd::cmp::SimdPartialOrd as _;
98 use std::simd::*;
99
100 const LANE_WIDTH: usize = 32;
101 type Vector = Simd<u8, LANE_WIDTH>;
102
103 pub(super) fn simulate(input: &Grid<u8>, part_two: bool, limit: u8) -> u32 {
104 let width = 2 + (input.height as usize).next_multiple_of(LANE_WIDTH) as i32;
109 let height = 2 + input.width;
110 let mut grid = Grid::new(width, height, 0);
111
112 for y in 0..input.height {
113 for x in 0..input.width {
114 let from = Point::new(x, y);
115 let to = Point::new(y + 1, x + 1);
116 grid[to] = u8::from(input[from] == SEAT);
117 }
118 }
119
120 let mut visible = Vec::new();
122
123 if part_two {
124 for y in 0..height {
125 for x in 0..width {
126 let from = Point::new(x, y);
127 if grid[from] == 0 {
128 continue;
129 }
130
131 for direction in DIAGONAL {
132 if grid[from + direction] == 1 {
133 continue;
134 }
135
136 let mut to = from + direction * 2;
137 while grid.contains(to) {
138 if grid[to] == 1 {
139 visible.push((from, to));
140 break;
141 }
142 to += direction;
143 }
144 }
145 }
146 }
147 }
148
149 let zero = Simd::splat(0);
151 let one = Simd::splat(1);
152 let limit = Simd::splat(limit);
153
154 let mut current = grid.same_size_with(0);
155 let mut next = grid.same_size_with(0);
156 let mut extra = grid.same_size_with(0);
157
158 loop {
159 if part_two {
161 extra.bytes.fill(0);
162 for &(from, to) in &visible {
163 extra[to] += current[from];
164 }
165 }
166
167 for x in (1..width - 1).step_by(LANE_WIDTH) {
169 let mut above = horizontal_neighbors(¤t, x, 0);
170 let mut row = horizontal_neighbors(¤t, x, 1);
171
172 for y in 1..height - 1 {
173 let index = (width * y + x) as usize;
174 let seats = Simd::from_slice(&grid.bytes[index..]);
175 let occupied = Simd::from_slice(¤t.bytes[index..]);
176 let extra = Simd::from_slice(&extra.bytes[index..]);
177
178 let below = horizontal_neighbors(¤t, x, y + 1);
179 let total = row + above + below + extra;
180 above = row;
181 row = below;
182
183 let first = total.simd_eq(zero).select(one, zero);
185 let second = total.simd_le(limit).select(occupied, zero);
187 let result = (first + second) & seats;
189
190 result.copy_to_slice(&mut next.bytes[index..]);
191 }
192 }
193
194 (current, next) = (next, current);
195 if current == next {
196 return current.bytes.iter().map(|&b| b as u32).sum();
197 }
198 }
199 }
200
201 #[inline]
203 fn horizontal_neighbors(grid: &Grid<u8>, x: i32, y: i32) -> Vector {
204 let index = (grid.width * y + x) as usize;
205
206 let center = Simd::from_slice(&grid.bytes[index..]);
207 let left = center.shift_elements_left::<1>(grid.bytes[index + LANE_WIDTH]);
208 let right = center.shift_elements_right::<1>(grid.bytes[index - 1]);
209
210 center + left + right
211 }
212}