use std::io; use std::io::BufRead; #[derive(Copy, Clone)] enum Opcode { Addr, Addi, Mulr, Muli, Banr, Bani, Borr, Bori, Setr, Seti, Gtir, Gtri, Gtrr, Eqir, Eqri, Eqrr, } impl Opcode { fn from_id(id: u16) -> Opcode { use self::Opcode::*; static VALUES: [Opcode; 16] = [Addr, Addi, Mulr, Muli, Banr, Bani, Borr, Bori, Setr, Seti, Gtir, Gtri, Gtrr, Eqir, Eqri, Eqrr]; VALUES[id as usize] } } struct Instr { op: usize, a: usize, b: usize, c: usize, } impl Instr { fn from(line: &str) -> Self { let mut iter = line.split(' ').map(|s| s.parse().unwrap()); let op = iter.next().unwrap(); let a = iter.next().unwrap(); let b = iter.next().unwrap(); let c = iter.next().unwrap(); Instr { op, a, b, c } } } #[derive(PartialEq, Eq, Clone)] struct Machine { regs: [usize; 4], } impl Machine { fn from(line: &str) -> Self { let mut machine = Machine { regs: [0; 4] }; let mut i = 0; for num in line.split(", ").map(|s| s.parse().unwrap()) { machine.regs[i] = num; i += 1; } machine } fn exec(&mut self, op: Opcode, a: usize, b: usize, c: usize) { match op { Opcode::Addr => self.regs[c] = self.regs[a] + self.regs[b], Opcode::Addi => self.regs[c] = self.regs[a] + b, Opcode::Mulr => self.regs[c] = self.regs[a] * self.regs[b], Opcode::Muli => self.regs[c] = self.regs[a] * b, Opcode::Banr => self.regs[c] = self.regs[a] & self.regs[b], Opcode::Bani => self.regs[c] = self.regs[a] & b, Opcode::Borr => self.regs[c] = self.regs[a] | self.regs[b], Opcode::Bori => self.regs[c] = self.regs[a] | b, Opcode::Setr => self.regs[c] = self.regs[a], Opcode::Seti => self.regs[c] = a, Opcode::Gtir => self.regs[c] = if a > self.regs[b] { 1 } else { 0 }, Opcode::Gtri => self.regs[c] = if self.regs[a] > b { 1 } else { 0 }, Opcode::Gtrr => self.regs[c] = if self.regs[a] > self.regs[b] { 1 } else { 0 }, Opcode::Eqir => self.regs[c] = if a == self.regs[b] { 1 } else { 0 }, Opcode::Eqri => self.regs[c] = if self.regs[a] == b { 1 } else { 0 }, Opcode::Eqrr => self.regs[c] = if self.regs[a] == self.regs[b] { 1 } else { 0 }, } } } struct Sample { before: Machine, after: Machine, instr: Instr, } impl Sample { fn behaves_as(&self) -> u16 { let mut r = 0; for id in 0..16 { let op = Opcode::from_id(id); let mut m = self.before.clone(); m.exec(op, self.instr.a, self.instr.b, self.instr.c); if m == self.after { r |= 1 << id; } } r } } fn parse_input(reader: T) -> io::Result<(Vec, Vec)> { let mut line_iter = reader.lines(); let mut samples = Vec::new(); let program; loop { let line = line_iter.next().unwrap()?; if line.len() == 0 { line_iter.next(); program = line_iter.map(|l| Instr::from(&l.unwrap())).collect::>(); break; } else if &line[..1] == "B" { let before = Machine::from(&line[9..line.len()-1]); let line = line_iter.next().unwrap()?; let instr = Instr::from(&line); let line = line_iter.next().unwrap()?; let after = Machine::from(&line[9..line.len()-1]); samples.push(Sample { before, after, instr }); line_iter.next(); } else { assert!(false); } } Ok((samples, program)) } fn resolve_helper(masks: &[u16; 16], res: &mut [u16; 16], at: usize, available: u16) -> bool { if at == 16 { return true; } let mut walker = available; while walker != 0 { let n = walker.trailing_zeros(); let bit = 1 << n; walker &= !bit; if (masks[at] & bit) != 0 { res[at] = n as u16; if resolve_helper(masks, res, at + 1, available & !bit) { return true; } } } false } fn resolve_mapping(masks: &[u16; 16]) -> [u16; 16] { let mut res = [0; 16]; assert!(resolve_helper(masks, &mut res, 0, 0xffff)); res } pub fn main(reader: T) -> io::Result<(String, String)> { let (samples, program) = parse_input(reader)?; let mut num_geq_3 = 0; let mut code_masks = [0xffff; 16]; for sample in samples { let mask = sample.behaves_as(); if mask.count_ones() >= 3 { num_geq_3 += 1; } code_masks[sample.instr.op] &= mask; } let part1 = num_geq_3; let mapping = resolve_mapping(&code_masks); let mut m = Machine { regs: [0; 4] }; for instr in program { m.exec(Opcode::from_id(mapping[instr.op]), instr.a, instr.b, instr.c); } let part2 = m.regs[0]; Ok((part1.to_string(), part2.to_string())) }