1use utils::bit::{bitwise_count4, bitwise_count8};
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day24 {
7 initial: u32,
8 recursive_minutes: usize,
9}
10
11const SIDE: usize = 5;
12const TILE_COUNT: usize = SIDE * SIDE;
13const MIDDLE: usize = TILE_COUNT / 2;
14const GRID_MASK: u32 = (1 << TILE_COUNT) - 1;
15const EXAMPLE_MINUTES: usize = 10;
16const REAL_MINUTES: usize = 200;
17const LEVELS: usize = 2 * REAL_MINUTES + 3;
18const ROW_MASK: u32 = 0b1_1111;
19const COLUMN_MASK: u32 = 0b00001_00001_00001_00001_00001;
20
21impl Day24 {
22 pub fn new(input: &str, input_type: InputType) -> Result<Self, InputError> {
23 let mut initial = 0;
24 let mut lines = input.lines();
25
26 for row in 0..SIDE {
27 let Some(line) = lines.next() else {
28 return Err(InputError::new(input, 0, "expected exactly 5 rows"));
29 };
30 if line.len() != SIDE {
31 return Err(InputError::new(input, line, "expected exactly 5 columns"));
32 }
33
34 for (column, &byte) in line.as_bytes().iter().enumerate() {
35 match byte {
36 b'.' => {}
37 b'#' => initial |= 1 << (row * SIDE + column),
38 _ => {
39 return Err(InputError::new(
40 input,
41 &line.as_bytes()[column..column + 1],
42 "expected '.' or '#'",
43 ));
44 }
45 }
46 }
47
48 if row == SIDE / 2 && Self::bug_at(initial, MIDDLE) != 0 {
49 return Err(InputError::new(
50 input,
51 &line.as_bytes()[SIDE / 2..],
52 "expected '.' in the middle",
53 ));
54 }
55 }
56 if let Some(line) = lines.next() {
57 return Err(InputError::new(input, line, "expected exactly 5 rows"));
58 }
59
60 Ok(Self {
61 initial,
62 recursive_minutes: match input_type {
63 InputType::Example => EXAMPLE_MINUTES,
64 InputType::Real => REAL_MINUTES,
65 },
66 })
67 }
68
69 #[must_use]
70 pub fn part1(&self) -> u32 {
71 let (mut power, mut lambda) = (1, 1);
74 let mut tortoise = self.initial;
75 let mut hare = Self::flat_step(self.initial);
76
77 while tortoise != hare {
78 if power == lambda {
79 tortoise = hare;
80 power *= 2;
81 lambda = 0;
82 }
83 hare = Self::flat_step(hare);
84 lambda += 1;
85 }
86
87 tortoise = self.initial;
88 hare = self.initial;
89 for _ in 0..lambda {
90 hare = Self::flat_step(hare);
91 }
92 while tortoise != hare {
93 tortoise = Self::flat_step(tortoise);
94 hare = Self::flat_step(hare);
95 }
96
97 tortoise
98 }
99
100 #[inline]
101 fn flat_step(bugs: u32) -> u32 {
102 let [ones, twos, fours] = bitwise_count4(&Self::cardinal_neighbors(bugs));
103 Self::next_layout(bugs, [ones, twos, fours, 0]) & GRID_MASK
104 }
105
106 #[inline]
107 fn cardinal_neighbors(current: u32) -> [u32; 4] {
108 let left = (current & !(COLUMN_MASK << (SIDE - 1))) << 1;
109 let right = (current & !COLUMN_MASK) >> 1;
110 let above = current << SIDE;
111 let below = current >> SIDE;
112 [left, right, above, below]
113 }
114
115 #[inline]
116 fn next_layout(current: u32, [ones, twos, fours, eights]: [u32; 4]) -> u32 {
117 let at_least_three = (ones & twos) | fours | eights;
118 let exactly_one = ones & !at_least_three;
119 let exactly_two = twos & !at_least_three;
120 exactly_one | (!current & exactly_two)
121 }
122
123 #[must_use]
124 pub fn part2(&self) -> u32 {
125 let mut first = [0; LEVELS];
126 let mut second = [0; LEVELS];
127 let (mut levels, mut next) = (&mut first, &mut second);
128
129 let level0 = REAL_MINUTES + 1;
130 let mut start = level0 - 1;
131 let mut end = level0 + 1;
132
133 levels[level0] = self.initial;
134
135 for _ in 0..self.recursive_minutes {
136 for (next_level, adjacent) in next[start..=end]
137 .iter_mut()
138 .zip(levels[start - 1..=end + 1].windows(3))
139 {
140 *next_level = Self::recursive_step(adjacent[0], adjacent[1], adjacent[2]);
141 }
142
143 start -= usize::from(next[start] != 0);
144 end += usize::from(next[end] != 0);
145 (levels, next) = (next, levels);
146 }
147
148 levels[start..=end]
149 .iter()
150 .copied()
151 .map(u32::count_ones)
152 .sum()
153 }
154
155 #[inline]
156 fn recursive_step(outer: u32, current: u32, inner: u32) -> u32 {
157 let [mut left, mut right, mut above, mut below] = Self::cardinal_neighbors(current);
167
168 left |= Self::bug_at(outer, MIDDLE - 1) * COLUMN_MASK;
171 right |= Self::bug_at(outer, MIDDLE + 1) * (COLUMN_MASK << (SIDE - 1));
172 above |= Self::bug_at(outer, MIDDLE - SIDE) * ROW_MASK;
173 below |= Self::bug_at(outer, MIDDLE + SIDE) * (ROW_MASK << (TILE_COUNT - SIDE));
174
175 let [l, r, a, b] = Self::inner_edge_neighbors(inner, 0);
180 left |= l;
181 right |= r;
182 above |= a;
183 below |= b;
184
185 let mut additional = [0; 4];
186 for (mask, i) in additional.iter_mut().zip(1..SIDE) {
187 let [l, r, a, b] = Self::inner_edge_neighbors(inner, i);
188 *mask = l | r | a | b;
189 }
190
191 Self::next_layout(
193 current,
194 bitwise_count8(&[
195 left,
196 right,
197 above,
198 below,
199 additional[0],
200 additional[1],
201 additional[2],
202 additional[3],
203 ]),
204 ) & (GRID_MASK ^ (1 << MIDDLE))
205 }
206
207 #[inline]
209 fn inner_edge_neighbors(inner: u32, i: usize) -> [u32; 4] {
210 [
211 Self::bug_at(inner, i * SIDE + SIDE - 1) << (MIDDLE + 1),
212 Self::bug_at(inner, i * SIDE) << (MIDDLE - 1),
213 Self::bug_at(inner, TILE_COUNT - SIDE + i) << (MIDDLE + SIDE),
214 Self::bug_at(inner, i) << (MIDDLE - SIDE),
215 ]
216 }
217
218 #[inline]
219 fn bug_at(bugs: u32, tile: usize) -> u32 {
220 (bugs >> tile) & 1
221 }
222}
223
224examples!(Day24 -> (u32, u32) [
225 {input: "....#\n#..#.\n#..##\n..#..\n#....", part1: 2_129_920, part2: 99},
226]);