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 impl Seat {
40 fn push(&mut self, index: Point) {
41 self.neighbors[self.size] = index;
42 self.size += 1;
43 }
44 }
45
46 pub(super) fn simulate(input: &Grid<u8>, part_two: bool, limit: u8) -> u32 {
47 let mut seats = Vec::new();
48
49 for y in 0..input.height {
50 for x in 0..input.width {
51 let point = Point::new(x, y);
52 if input[point] != SEAT {
53 continue;
54 }
55
56 let mut seat = Seat { point, size: 0, neighbors: [ORIGIN; 8] };
57
58 for direction in DIAGONAL {
59 if part_two {
60 let mut next = point + direction;
61 while input.contains(next) {
62 if input[next] == SEAT {
63 seat.push(next);
64 break;
65 }
66 next += direction;
67 }
68 } else {
69 let next = point + direction;
70 if input.contains(next) && input[next] == SEAT {
71 seat.push(next);
72 }
73 }
74 }
75
76 seats.push(seat);
77 }
78 }
79
80 let mut current = input.same_size_with(0);
81 let mut next = input.same_size_with(0);
82
83 loop {
84 for seat in &seats {
85 let total: u8 = seat.neighbors[0..seat.size].iter().map(|&i| current[i]).sum();
86
87 next[seat.point] = if current[seat.point] == 0 {
88 u8::from(total == 0)
89 } else {
90 u8::from(total < limit)
91 };
92 }
93
94 (current, next) = (next, current);
95 if current == next {
96 return current.bytes.iter().map(|&n| n as u32).sum();
97 }
98 }
99 }
100}
101
102#[cfg(feature = "simd")]
103mod implementation {
104 use super::*;
105 use std::simd::cmp::SimdPartialEq as _;
106 use std::simd::cmp::SimdPartialOrd as _;
107 use std::simd::*;
108
109 const LANE_WIDTH: usize = 32;
110 type Vector = Simd<u8, LANE_WIDTH>;
111
112 pub(super) fn simulate(input: &Grid<u8>, part_two: bool, limit: u8) -> u32 {
113 let width = 2 + (input.height as usize).next_multiple_of(LANE_WIDTH) as i32;
118 let height = 2 + input.width;
119 let mut grid = Grid::new(width, height, 0);
120
121 for y in 0..input.height {
122 for x in 0..input.width {
123 let from = Point::new(x, y);
124 let to = Point::new(y + 1, x + 1);
125 grid[to] = u8::from(input[from] == SEAT);
126 }
127 }
128
129 let mut visible = Vec::new();
131
132 if part_two {
133 for y in 0..height {
134 for x in 0..width {
135 let from = Point::new(x, y);
136 if grid[from] == 0 {
137 continue;
138 }
139
140 for direction in DIAGONAL {
141 if grid[from + direction] == 1 {
142 continue;
143 }
144
145 let mut to = from + direction * 2;
146 while grid.contains(to) {
147 if grid[to] == 1 {
148 visible.push((from, to));
149 break;
150 }
151 to += direction;
152 }
153 }
154 }
155 }
156 }
157
158 let zero = Simd::splat(0);
160 let one = Simd::splat(1);
161 let limit = Simd::splat(limit);
162
163 let mut current = grid.same_size_with(0);
164 let mut next = grid.same_size_with(0);
165 let mut extra = grid.same_size_with(0);
166
167 loop {
168 if part_two {
170 extra.bytes.fill(0);
171 for &(from, to) in &visible {
172 extra[to] += current[from];
173 }
174 }
175
176 for x in (1..width - 1).step_by(LANE_WIDTH) {
178 let mut above = horizontal_neighbors(¤t, x, 0);
179 let mut row = horizontal_neighbors(¤t, x, 1);
180
181 for y in 1..height - 1 {
182 let index = (width * y + x) as usize;
183 let seats = Simd::from_slice(&grid.bytes[index..]);
184 let occupied = Simd::from_slice(¤t.bytes[index..]);
185 let extra = Simd::from_slice(&extra.bytes[index..]);
186
187 let below = horizontal_neighbors(¤t, x, y + 1);
188 let total = row + above + below + extra;
189 above = row;
190 row = below;
191
192 let first = total.simd_eq(zero).select(one, zero);
194 let second = total.simd_le(limit).select(occupied, zero);
196 let result = (first + second) & seats;
198
199 result.copy_to_slice(&mut next.bytes[index..]);
200 }
201 }
202
203 (current, next) = (next, current);
204 if current == next {
205 return current.bytes.iter().map(|&b| b as u32).sum();
206 }
207 }
208 }
209
210 #[inline]
212 fn horizontal_neighbors(grid: &Grid<u8>, x: i32, y: i32) -> Vector {
213 let index = (grid.width * y + x) as usize;
214
215 let center = Simd::from_slice(&grid.bytes[index..]);
216 let left = center.shift_elements_left::<1>(grid.bytes[index + LANE_WIDTH]);
217 let right = center.shift_elements_right::<1>(grid.bytes[index - 1]);
218
219 center + left + right
220 }
221}