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