1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
7pub struct Day16 {
8    samples: Vec<(u32, u16)>,
9    instructions: Vec<(u32, [u32; 3])>,
10}
11
12utils::enumerable_enum! {
13    #[repr(u32)]
14    #[derive(Copy, Clone, Debug)]
15    enum Operation {
16        Addr,
17        Addi,
18        Mulr,
19        Muli,
20        Banr,
21        Bani,
22        Borr,
23        Bori,
24        Setr,
25        Seti,
26        Gtir,
27        Gtri,
28        Gtrr,
29        Eqir,
30        Eqri,
31        Eqrr,
32    }
33}
34
35impl Day16 {
36    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
37        let registers = parser::digit().map(u32::from).repeat_n::<4, _>(", ");
38        let instruction = parser::number_range(0u32..=15)
39            .with_suffix(b' ')
40            .then(parser::number_range(0u32..=3).repeat_n::<3, _>(b' '));
41        let sample = registers
42            .with_prefix("Before: [")
43            .with_suffix("]".then(parser::eol()))
44            .then(instruction.with_suffix(parser::eol()))
45            .then(
46                registers
47                    .with_prefix("After:  [")
48                    .with_suffix("]".then(parser::eol())),
49            )
50            .map(|(before, (opcode, [a, b, c]), after)| {
51                (
52                    opcode,
53                    Operation::iter().fold(0, |acc, op| {
56                        let possible = Self::execute(op, a, b, &before) == after[c as usize];
57                        acc | (possible as u16) << op as u32
58                    }),
59                )
60            });
61
62        let (samples, instructions) = sample
63            .repeat(parser::eol(), 1)
64            .with_eol()
65            .with_eol()
66            .with_eol()
67            .then(instruction.repeat(parser::eol(), 1))
68            .parse_complete(input)?;
69
70        Ok(Self {
71            samples,
72            instructions,
73        })
74    }
75
76    #[must_use]
77    pub fn part1(&self) -> usize {
78        self.samples
79            .iter()
80            .filter(|&(_, mask)| mask.count_ones() >= 3)
81            .count()
82    }
83
84    #[must_use]
85    pub fn part2(&self) -> u32 {
86        let mut op_masks = [u16::MAX; 16];
87        for &(opcode, mask) in &self.samples {
88            op_masks[opcode as usize] &= mask;
89        }
90
91        let mut ops = [Operation::Addi; 16];
92        for _ in 0..16 {
93            let opcode = op_masks
94                .iter()
95                .position(|&mask| mask.count_ones() == 1)
96                .expect("no solution found: all remaining opcodes could be multiple operations");
97
98            let mask = op_masks[opcode];
99            ops[opcode] = Operation::from_discriminant(mask.trailing_zeros());
100
101            op_masks.iter_mut().for_each(|m| *m &= !mask);
102        }
103
104        let mut registers = [0; 4];
105        for &(opcode, [a, b, c]) in &self.instructions {
106            registers[c as usize] = Self::execute(ops[opcode as usize], a, b, ®isters);
107        }
108        registers[0]
109    }
110
111    #[inline]
112    fn execute(op: Operation, a: u32, b: u32, registers: &[u32; 4]) -> u32 {
113        match op {
114            Operation::Addr => registers[a as usize] + registers[b as usize],
115            Operation::Addi => registers[a as usize] + b,
116            Operation::Mulr => registers[a as usize] * registers[b as usize],
117            Operation::Muli => registers[a as usize] * b,
118            Operation::Banr => registers[a as usize] & registers[b as usize],
119            Operation::Bani => registers[a as usize] & b,
120            Operation::Borr => registers[a as usize] | registers[b as usize],
121            Operation::Bori => registers[a as usize] | b,
122            Operation::Setr => registers[a as usize],
123            Operation::Seti => a,
124            Operation::Gtir => u32::from(a > registers[b as usize]),
125            Operation::Gtri => u32::from(registers[a as usize] > b),
126            Operation::Gtrr => u32::from(registers[a as usize] > registers[b as usize]),
127            Operation::Eqir => u32::from(a == registers[b as usize]),
128            Operation::Eqri => u32::from(registers[a as usize] == b),
129            Operation::Eqrr => u32::from(registers[a as usize] == registers[b as usize]),
130        }
131    }
132}
133
134examples!(Day16 -> (usize, u32) []);