1use crate::util::parse::*;
45use std::array::from_fn;
46
47pub struct Tile {
48 id: u64,
49 top: [usize; 8],
50 left: [usize; 8],
51 bottom: [usize; 8],
52 right: [usize; 8],
53 pixels: [[u8; 10]; 10],
54}
55
56impl Tile {
57 const COEFFICIENTS: [[i32; 6]; 8] = [
63 [1, 0, 1, 0, 1, 1],
64 [-1, 0, 8, 0, 1, 1],
65 [1, 0, 1, 0, -1, 8],
66 [-1, 0, 8, 0, -1, 8],
67 [0, 1, 1, -1, 0, 8],
68 [0, 1, 1, 1, 0, 1],
69 [0, -1, 8, -1, 0, 8],
70 [0, -1, 8, 1, 0, 1],
71 ];
72
73 fn from(chunk: &[&str]) -> Tile {
74 let id = (&chunk[0][5..9]).unsigned();
75
76 let pixels: [[u8; 10]; 10] = from_fn(|i| chunk[i + 1].as_bytes().try_into().unwrap());
77
78 let binary = |row: usize, col: usize| (pixels[row][col] & 1) as usize;
81 let (t, l, b, r) = (0..10).fold((0, 0, 0, 0), |(t, l, b, r), i| {
82 (
83 (t << 1) | binary(0, i),
84 (l << 1) | binary(i, 0),
85 (b << 1) | binary(9, i),
86 (r << 1) | binary(i, 9),
87 )
88 });
89
90 let reverse = |edge: usize| edge.reverse_bits() >> 54;
91 let rt = reverse(t);
92 let rl = reverse(l);
93 let rb = reverse(b);
94 let rr = reverse(r);
95
96 let top = [t, rt, b, rb, rl, l, rr, r];
99 let left = [l, r, rl, rr, b, t, rb, rt];
100 let bottom = [b, rb, t, rt, rr, r, rl, l];
101 let right = [r, l, rr, rl, t, b, rt, rb];
102
103 Tile { id, top, left, bottom, right, pixels }
104 }
105
106 fn transform(&self, image: &mut [u128], permutation: usize) {
108 let [a, b, c, d, e, f] = Self::COEFFICIENTS[permutation];
109
110 for row in 0..8 {
111 let mut acc = 0;
112
113 for col in 0..8 {
114 let x = a * col + b * row + c;
115 let y = d * col + e * row + f;
116 let b = self.pixels[y as usize][x as usize];
117 acc = (acc << 1) | (b & 1);
118 }
119
120 image[row as usize] = (image[row as usize] << 8) | (acc as u128);
121 }
122 }
123}
124
125pub fn parse(input: &str) -> Vec<Tile> {
126 let lines: Vec<_> = input.lines().collect();
127 lines.chunks(12).map(Tile::from).collect()
128}
129
130pub fn part1(input: &[Tile]) -> u64 {
131 let mut freq = [0; 1024];
132 let mut result = 1;
133
134 for tile in input {
135 for edge in tile.top {
136 freq[edge] += 1;
137 }
138 }
139
140 for tile in input {
141 let total =
143 freq[tile.top[0]] + freq[tile.left[0]] + freq[tile.bottom[0]] + freq[tile.right[0]];
144 if total == 6 {
145 result *= tile.id;
146 }
147 }
148
149 result
150}
151
152pub fn part2(input: &[Tile]) -> u32 {
153 let mut edge_to_tile = [[0; 2]; 1024];
156 let mut freq = [0; 1024];
157 let mut placed = [false; 1024];
158
159 for (i, tile) in input.iter().enumerate() {
160 for edge in tile.top {
161 edge_to_tile[edge][freq[edge]] = i;
162 freq[edge] += 1;
163 }
164 }
165
166 let mut find_arbitrary_corner = || {
167 for tile in input {
168 for j in 0..8 {
169 if freq[tile.top[j]] == 1 && freq[tile.left[j]] == 1 {
170 freq[tile.top[j]] += 1;
171 return tile.top[j];
172 }
173 }
174 }
175 unreachable!()
176 };
177 let mut find_matching_tile = |edge: usize| {
178 let [first, second] = edge_to_tile[edge];
179 let next = if placed[first] { second } else { first };
180 placed[next] = true;
181 &input[next]
182 };
183
184 let mut next_top = find_arbitrary_corner();
186 let mut image = [0; 96];
187 let mut index = 0;
188
189 while freq[next_top] == 2 {
190 let tile = find_matching_tile(next_top);
191 let permutation = (0..8).position(|i| tile.top[i] == next_top).unwrap();
192 tile.transform(&mut image[index..], permutation);
193 next_top = tile.bottom[permutation];
194
195 let mut next_left = tile.right[permutation];
196
197 while freq[next_left] == 2 {
198 let tile = find_matching_tile(next_left);
199 let permutation = (0..8).position(|i| tile.left[i] == next_left).unwrap();
200 tile.transform(&mut image[index..], permutation);
201 next_left = tile.right[permutation];
202 }
203
204 index += 8;
205 }
206
207 let sea: u32 = image.iter().map(|n| n.count_ones()).sum();
209 let find = |monster: &mut [u128], width: usize, height: usize| {
210 let mut rough = sea;
211
212 for _ in 0..(96 - width + 1) {
213 for window in image.windows(height) {
214 if monster.iter().enumerate().all(|(i, &n)| n & window[i] == n) {
215 rough -= 15;
216 }
217 }
218 monster.iter_mut().for_each(|n| *n <<= 1);
219 }
220
221 (rough < sea).then_some(rough)
222 };
223
224 let mut monsters = [
227 [0b00000000000000000010, 0b10000110000110000111, 0b01001001001001001000],
228 [0b01001001001001001000, 0b10000110000110000111, 0b00000000000000000010],
229 [0b01000000000000000000, 0b11100001100001100001, 0b00010010010010010010],
230 [0b00010010010010010010, 0b11100001100001100001, 0b01000000000000000000],
231 ];
232
233 for monster in &mut monsters {
234 if let Some(rough) = find(monster, 20, 3) {
235 return rough;
236 }
237 }
238
239 let mut monsters = [
241 [2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 3, 2],
242 [2, 3, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2],
243 [2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 6, 2],
244 [2, 6, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2],
245 ];
246
247 for monster in &mut monsters {
248 if let Some(rough) = find(monster, 3, 20) {
249 return rough;
250 }
251 }
252
253 unreachable!()
254}