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