1use std::array::from_fn;
45
46use crate::util::parse::*;
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]) -> Self {
75 let id = chunk[0][5..9].unsigned();
76
77 let pixels: [[_; 10]; 10] = from_fn(|i| chunk[i + 1].as_bytes().try_into().unwrap());
78
79 let binary = |row: usize, col: usize| usize::from(pixels[row][col] & 1);
82 let (t, l, b, r) = (0..10).fold((0, 0, 0, 0), |(t, l, b, r), i| {
83 (
84 (t << 1) | binary(0, i),
85 (l << 1) | binary(i, 0),
86 (b << 1) | binary(9, i),
87 (r << 1) | binary(i, 9),
88 )
89 });
90
91 let reverse = |edge: usize| edge.reverse_bits() >> 54;
92 let rt = reverse(t);
93 let rl = reverse(l);
94 let rb = reverse(b);
95 let rr = reverse(r);
96
97 let top = [t, rt, b, rb, rl, l, rr, r];
100 let left = [l, r, rl, rr, b, t, rb, rt];
101 let bottom = [b, rb, t, rt, rr, r, rl, l];
102 let right = [r, l, rr, rl, t, b, rt, rb];
103
104 Self { id, top, left, bottom, right, pixels }
105 }
106
107 fn transform(&self, image: &mut [u128], permutation: usize) {
109 let [a, b, c, d, e, f] = Self::COEFFICIENTS[permutation];
110
111 for row in 0..8 {
112 let acc = (0..8).fold(0, |acc, col| {
113 let x = a * col + b * row + c;
114 let y = d * col + e * row + f;
115 let b = self.pixels[y as usize][x as usize];
116 (acc << 1) | (b & 1)
117 });
118
119 image[row as usize] = (image[row as usize] << 8) | u128::from(acc);
120 }
121 }
122}
123
124pub fn parse(input: &str) -> Vec<Tile> {
125 let lines: Vec<_> = input.lines().collect();
126 lines.chunks(12).map(Tile::from).collect()
127}
128
129pub fn part1(input: &[Tile]) -> u64 {
130 let mut freq = [0; 1024];
131
132 for edge in input.iter().flat_map(|t| t.top) {
133 freq[edge] += 1;
134 }
135
136 input
138 .iter()
139 .filter(|t| freq[t.top[0]] + freq[t.left[0]] + freq[t.bottom[0]] + freq[t.right[0]] == 6)
140 .map(|t| t.id)
141 .product()
142}
143
144pub fn part2(input: &[Tile]) -> u32 {
145 let mut edge_to_tile = [[0; 2]; 1024];
148 let mut freq = [0; 1024];
149 let mut placed = [false; 1024];
150
151 for (i, tile) in input.iter().enumerate() {
152 for edge in tile.top {
153 edge_to_tile[edge][freq[edge]] = i;
154 freq[edge] += 1;
155 }
156 }
157
158 let mut next_top = input
159 .iter()
160 .flat_map(|tile| tile.top.iter().zip(&tile.left))
161 .find_map(|(&top, &left)| (freq[top] == 1 && freq[left] == 1).then_some(top))
162 .unwrap();
163 freq[next_top] += 1;
164
165 let mut find_matching_tile = |edge| {
166 let [first, second] = edge_to_tile[edge];
167 let next = if placed[first] { second } else { first };
168 placed[next] = true;
169 &input[next]
170 };
171
172 let mut image = [0; 96];
174 let mut index = 0;
175
176 while freq[next_top] == 2 {
177 let tile = find_matching_tile(next_top);
178 let permutation = tile.top.iter().position(|&top| top == next_top).unwrap();
179 tile.transform(&mut image[index..], permutation);
180 next_top = tile.bottom[permutation];
181
182 let mut next_left = tile.right[permutation];
183
184 while freq[next_left] == 2 {
185 let tile = find_matching_tile(next_left);
186 let permutation = tile.left.iter().position(|&left| left == next_left).unwrap();
187 tile.transform(&mut image[index..], permutation);
188 next_left = tile.right[permutation];
189 }
190
191 index += 8;
192 }
193
194 let sea = image.iter().map(|n| n.count_ones()).sum();
196 let find = |monster: &mut [u128], width: usize, height: usize| {
197 let mut rough = sea;
198
199 for _ in 0..(96 - width + 1) {
200 for window in image.windows(height) {
201 if monster.iter().zip(window).all(|(&mask, &row)| mask & row == mask) {
202 rough -= 15;
203 }
204 }
205 monster.iter_mut().for_each(|n| *n <<= 1);
206 }
207
208 (rough < sea).then_some(rough)
209 };
210
211 let mut monsters = [
214 [0b00000000000000000010, 0b10000110000110000111, 0b01001001001001001000],
215 [0b01001001001001001000, 0b10000110000110000111, 0b00000000000000000010],
216 [0b01000000000000000000, 0b11100001100001100001, 0b00010010010010010010],
217 [0b00010010010010010010, 0b11100001100001100001, 0b01000000000000000000],
218 ];
219
220 if let Some(rough) = monsters.iter_mut().find_map(|monster| find(monster, 20, 3)) {
221 return rough;
222 }
223
224 let mut monsters = [
226 [2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 3, 2],
227 [2, 3, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2, 2, 4, 0, 0, 4, 2],
228 [2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 6, 2],
229 [2, 6, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2, 2, 1, 0, 0, 1, 2],
230 ];
231
232 monsters.iter_mut().find_map(|monster| find(monster, 3, 20)).unwrap()
233}