1use crate::elfcode::{HookControlFlow, Instruction, Interpreter, Register};
2use std::collections::HashSet;
3use utils::prelude::*;
4
5#[derive(Clone, Debug)]
9pub struct Day21 {
10 interpreter: Interpreter,
11}
12
13impl Day21 {
14 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
15 let interpreter = Interpreter::new(input)?;
16
17 let instruction_pointer = interpreter.instruction_pointer();
18 if instruction_pointer == Register::A {
19 return Err(InputError::new(input, 0, "expected #ip to be non-zero"));
20 }
21
22 let instructions = interpreter.instructions();
23 if instructions
24 .iter()
25 .flat_map(|i| i.registers())
26 .filter(|&r| r == Register::A)
27 .count()
28 != 1
29 {
30 return Err(InputError::new(
31 input,
32 0,
33 "expected exactly one use of register 0",
34 ));
35 }
36
37 if let [
38 ..,
39 Instruction::Eqrr(_, Register::A, tmp),
40 Instruction::Addr(tmp2, ip, ip2),
41 Instruction::Seti(_, ip3),
42 ] = *instructions
43 && tmp == tmp2
44 && ip == ip2
45 && ip == ip3
46 && ip == instruction_pointer
47 && tmp != instruction_pointer
48 {
49 Ok(Self { interpreter })
52 } else {
53 Err(InputError::new(
54 input,
55 input.len() - 1,
56 "expected register 0 matching to control termination of the outer loop",
57 ))
58 }
59 }
60
61 #[must_use]
62 pub fn part1(&self) -> u32 {
63 self.next(&mut [0; 6])
64 }
65
66 #[must_use]
67 pub fn part2(&self) -> u32 {
68 let mut reg = [0; 6];
71 let mut seen = HashSet::with_capacity(20000);
72 let mut last_first_seen = 0;
73 loop {
74 let target = self.next(&mut reg);
75 if seen.insert(target) {
76 last_first_seen = target;
77 } else {
78 break;
79 }
80 }
81 last_first_seen
82 }
83
84 fn next(&self, reg: &mut [u32; 6]) -> u32 {
85 let mut target = None;
86
87 self.interpreter
88 .run(reg, |instructions, instruction_pointer, reg| {
89 let addr = reg[instruction_pointer] as usize;
90
91 if let Instruction::Eqrr(tgt, Register::A, tmp) = instructions[addr] {
92 target = Some(reg[tgt]);
93
94 reg[tmp] = 0;
97 reg[instruction_pointer] += 1;
98
99 return HookControlFlow::Halt;
100 }
101
102 #[rustfmt::skip]
113 if let [
114 Instruction::Seti(0, quo),
115 Instruction::Addi(quo2, 1, tmp),
116 Instruction::Muli(tmp2, 256, tmp3),
117 Instruction::Gtrr(tmp4, num, tmp5),
118 Instruction::Addr(tmp6, ip, ip2),
119 Instruction::Addi(ip3, 1, ip4),
120 Instruction::Seti(end, ip5),
121 Instruction::Addi(quo3, 1, quo4),
122 Instruction::Seti(start, ip6),
123 ..,
124 ] = instructions[addr..]
125 && quo == quo2 && quo == quo3 && quo == quo4
126 && tmp == tmp2 && tmp == tmp3 && tmp == tmp4 && tmp == tmp5 && tmp == tmp6
127 && ip == ip2 && ip == ip3 && ip == ip4 && ip == ip5 && ip == ip6
128 && start as usize == addr
129 && end as usize == addr + 8
130 {
131 reg[quo] = reg[num] >> 8;
132 reg[tmp] = 1;
133 reg[ip] += 9;
134 return HookControlFlow::Next;
135 };
136
137 HookControlFlow::Execute
138 });
139
140 target.expect("no solution found")
141 }
142}
143
144examples!(Day21 -> (u32, u32) [
145 {
147 input: "#ip 4\n\
157 seti 0 0 2\n\
158 addi 2 1 2\n\
159 eqri 2 100 1\n\
160 addr 1 4 4\n\
161 addi 4 1 4\n\
162 seti 1 0 2\n\
163 eqrr 2 0 1\n\
164 addr 1 4 4\n\
165 seti 0 0 4",
166 part1: 1,
167 part2: 99,
168 },
169]);