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