1use std::collections::HashMap;
2use std::ops::ControlFlow;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
7pub struct Day24 {
8 wires: Vec<Wire>,
9 wire_names: Vec<[u8; 3]>,
10 x_initial: u64,
11 y_initial: u64,
12 z_indexes: Vec<usize>,
13}
14
15#[derive(Copy, Clone, Debug, PartialEq)]
16enum Wire {
17 X(usize),
18 Y(usize),
19 And(usize, usize),
20 Or(usize, usize),
21 Xor(usize, usize),
22}
23
24impl Day24 {
25 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
26 let Some((initial_str, gate_str)) = input.split_once("\n\n") else {
27 return Err(InputError::new(input, 0, "expected inputs and gates"));
28 };
29
30 let mut wires = Vec::new();
31 let mut wire_names = Vec::new();
32 let mut indexes = HashMap::new();
33 let mut x_initial = 0;
34 let mut y_initial = 0;
35 let mut input_bits = 64;
36
37 let mut next = (b'x', 0);
38 for item in parser::byte_range(b'x'..=b'y')
39 .then(parser::byte_range(b'0'..=b'9'))
40 .then(parser::byte_range(b'0'..=b'9'))
41 .with_suffix(": ")
42 .then(parser::byte_range(b'0'..=b'1'))
43 .with_consumed()
44 .with_suffix(parser::eol())
45 .parse_iterator(initial_str)
46 {
47 let ((wire, b), line) = item?;
48 let n = ((wire.1 - b'0') * 10 + (wire.2 - b'0')) as usize;
49
50 if (wire.0, n) != next {
51 if next.0 == b'x' && wire == (b'y', b'0', b'0') {
52 input_bits = next.1;
53 } else {
54 return Err(InputError::new(input, line, "unexpected initial value"));
55 }
56 }
57
58 if wire.0 == b'x' {
59 x_initial |= u64::from(b == b'1') << n;
60 wires.push(Wire::X(n));
61 } else {
62 y_initial |= u64::from(b == b'1') << n;
63 wires.push(Wire::Y(n));
64 }
65 wire_names.push(wire.into());
66 indexes.insert(wire.into(), wires.len() - 1);
67
68 if n == input_bits - 1 {
69 next = (b'?', 0);
70 } else {
71 next = (wire.0, n + 1);
72 }
73 }
74
75 let mut z_indexes = vec![usize::MAX; input_bits + 1];
76 let wire = parser::byte().repeat_n::<3, _>(parser::noop());
77 for item in wire
78 .then(parser::literal_map!(
79 " AND " => Wire::And as fn(usize, usize) -> Wire,
80 " OR " => Wire::Or,
81 " XOR " => Wire::Xor,
82 ))
83 .then(wire.with_suffix(" -> "))
84 .then(wire)
85 .with_suffix(parser::eol())
86 .parse_iterator(gate_str)
87 {
88 let (in1, gate, in2, out) = item?;
89
90 let mut index_of = |n| {
91 *indexes.entry(n).or_insert_with(|| {
92 wires.push(Wire::X(usize::MAX)); wire_names.push(n);
94 wires.len() - 1
95 })
96 };
97
98 let in1_index = index_of(in1);
99 let in2_index = index_of(in2);
100 let out_index = index_of(out);
101
102 if wires[out_index] != Wire::X(usize::MAX) {
103 return Err(InputError::new(input, 0, "duplicate wire definition"));
104 }
105 if out[0] == b'z' {
106 let index = ((out[1] - b'0') * 10 + (out[2] - b'0')) as usize;
107 if index < z_indexes.len() {
108 z_indexes[index] = out_index;
109 } else {
110 return Err(InputError::new(input, 0, "too many z outputs"));
111 }
112 }
113
114 wires[out_index] = gate(in1_index, in2_index);
115 }
116
117 if wires.contains(&Wire::X(usize::MAX)) {
118 return Err(InputError::new(input, 0, "undefined wire"));
119 }
120 if z_indexes.contains(&usize::MAX) {
121 return Err(InputError::new(input, 0, "undefined z output"));
122 }
123
124 Ok(Self {
125 wires,
126 wire_names,
127 x_initial,
128 y_initial,
129 z_indexes,
130 })
131 }
132
133 #[must_use]
134 pub fn part1(&self) -> u64 {
135 let mut z = 0;
136 let mut cache = vec![None; self.wires.len()];
137 for (i, &index) in self.z_indexes.iter().enumerate() {
138 z |= u64::from(Self::evaluate(
139 index,
140 &self.wires,
141 self.x_initial,
142 self.y_initial,
143 &mut cache,
144 )) << i;
145 }
146 z
147 }
148
149 fn evaluate(index: usize, wires: &[Wire], x: u64, y: u64, cache: &mut [Option<bool>]) -> bool {
150 if let Some(c) = cache[index] {
151 return c;
152 }
153 let v = match wires[index] {
154 Wire::X(n) => return x & (1 << n) != 0,
155 Wire::Y(n) => return y & (1 << n) != 0,
156 Wire::And(a, b) => {
157 Self::evaluate(a, wires, x, y, cache) && Self::evaluate(b, wires, x, y, cache)
158 }
159 Wire::Or(a, b) => {
160 Self::evaluate(a, wires, x, y, cache) || Self::evaluate(b, wires, x, y, cache)
161 }
162 Wire::Xor(a, b) => {
163 Self::evaluate(a, wires, x, y, cache) ^ Self::evaluate(b, wires, x, y, cache)
164 }
165 };
166 cache[index] = Some(v);
167 v
168 }
169
170 #[must_use]
171 pub fn part2(&self) -> String {
172 let mut test_cases = Vec::new();
173 for i in 0..self.z_indexes.len() - 1 {
174 test_cases.push((i, 1u64 << i, 0u64));
175 test_cases.push((i, 0u64, 1u64 << i));
176 test_cases.push((i + 1, 1u64 << i, 1u64 << i));
177 test_cases.push((i + 1, (1u64 << (i + 1)) - 1, 1u64));
178 test_cases.push((i + 1, 1u64, (1u64 << (i + 1)) - 1));
179 }
180
181 let mut wires = self.wires.clone();
182 if self.find_swaps(&test_cases, &mut wires, None).is_continue() {
183 panic!("failed to find working combination");
184 }
185
186 let mut changes = Vec::new();
187 for (i, (&wire, &orig)) in wires.iter().zip(&self.wires).enumerate() {
188 if wire != orig {
189 changes.push(self.wire_names[i]);
190 }
191 }
192 assert_eq!(changes.len(), 8, "found incorrect number of changes");
193
194 changes.sort_unstable();
195 changes.into_iter().fold(String::new(), |mut acc, name| {
196 if !acc.is_empty() {
197 acc.push(',');
198 }
199 acc.push(name[0] as char);
200 acc.push(name[1] as char);
201 acc.push(name[2] as char);
202 acc
203 })
204 }
205
206 fn find_swaps(
207 &self,
208 test_cases: &[(usize, u64, u64)],
209 wires: &mut [Wire],
210 last: Option<usize>,
211 ) -> ControlFlow<()> {
212 let mut cache = vec![None; wires.len()];
213 let mut used = vec![false; wires.len()];
214 for &(n, x, y) in test_cases {
215 if Some(n) < last {
216 continue;
219 }
220
221 cache.fill(None);
222 let sum = x + y;
223
224 for i in 0..n {
226 let b = Self::evaluate(self.z_indexes[i], wires, x, y, &mut cache);
227 if ((sum >> i) & 1 != 0) != b {
228 return ControlFlow::Continue(());
230 }
231 }
232
233 for i in 0..wires.len() {
235 used[i] = cache[i].is_some();
236 }
237
238 let b = Self::evaluate(self.z_indexes[n], wires, x, y, &mut cache);
239 if ((sum >> n) & 1 != 0) == b {
240 continue;
242 }
243
244 if last == Some(n) {
246 return ControlFlow::Continue(());
248 }
249
250 let candidates1 = cache
252 .iter()
253 .zip(&used)
254 .enumerate()
255 .filter(|&(_, (&c, &u))| c.is_some() && !u)
256 .map(|(i, _)| i)
257 .collect::<Vec<_>>();
258 let mask = (1 << (n + 1)) - 1;
260 let candidates2 = Self::used_bits(wires)
261 .iter()
262 .zip(&used)
263 .enumerate()
264 .filter(|&(_, (&b, &u))| b != 0 && b & mask == b && !u)
265 .map(|(i, _)| i)
266 .collect::<Vec<_>>();
267
268 for &c1 in &candidates1 {
270 for &c2 in &candidates2 {
271 if c1 == c2 {
272 continue;
273 }
274
275 (wires[c1], wires[c2]) = (wires[c2], wires[c1]);
276
277 if !self.loops(wires, &[c1, c2]) {
279 cache.fill(None);
282 let b = Self::evaluate(self.z_indexes[n], wires, x, y, &mut cache);
283 if ((sum >> n) & 1 != 0) == b
284 && self.find_swaps(test_cases, wires, Some(n)).is_break()
285 {
286 return ControlFlow::Break(());
288 }
289 }
290
291 (wires[c1], wires[c2]) = (wires[c2], wires[c1]);
293 }
294 }
295
296 return ControlFlow::Continue(());
298 }
299
300 ControlFlow::Break(())
301 }
302
303 fn used_bits(wires: &[Wire]) -> Vec<u64> {
304 fn eval(index: usize, wires: &[Wire], used_bits: &mut [u64]) -> u64 {
305 if used_bits[index] != 0 {
306 return used_bits[index];
307 }
308 let v = match wires[index] {
309 Wire::X(n) => return 1 << n,
310 Wire::Y(n) => return 1 << n,
311 Wire::And(a, b) | Wire::Or(a, b) | Wire::Xor(a, b) => {
312 eval(a, wires, used_bits) | eval(b, wires, used_bits)
313 }
314 };
315 used_bits[index] = v;
316 v
317 }
318
319 let mut used = vec![0; wires.len()];
320 for i in 0..wires.len() {
321 eval(i, wires, &mut used);
322 }
323 used
324 }
325
326 fn loops(&self, wires: &[Wire], to_check: &[usize]) -> bool {
327 #[derive(Copy, Clone, PartialEq)]
328 enum State {
329 Unvisited,
330 InStack,
331 Visited,
332 }
333
334 fn eval(index: usize, wires: &[Wire], states: &mut [State]) -> bool {
335 if states[index] != State::Unvisited {
336 return states[index] == State::InStack;
337 }
338 match wires[index] {
339 Wire::X(_) | Wire::Y(_) => {}
340 Wire::And(a, b) | Wire::Or(a, b) | Wire::Xor(a, b) => {
341 states[index] = State::InStack;
342 if eval(a, wires, states) || eval(b, wires, states) {
343 return true;
344 }
345 states[index] = State::Visited;
346 }
347 }
348 false
349 }
350
351 let mut states = vec![State::Unvisited; wires.len()];
352 to_check.iter().any(|&i| eval(i, wires, &mut states))
353 }
354}
355
356examples!(Day24 -> (u64, &'static str) []);