use advent_of_code::strings::{convert_to_array, parsenumber}; use advent_of_code_macros::include_aoc; use log::*; use thousands::Separable; include_aoc!(DATA 2024 17); const ADV: u64 = 0; const BXL: u64 = 1; const BST: u64 = 2; const JNZ: u64 = 3; const BXC: u64 = 4; const OUT: u64 = 5; const BDV: u64 = 6; const CDV: u64 = 7; const BASE: u64 = 2; #[derive(Debug, Clone)] struct GenericCPU { a: u64, b: u64, c: u64, pos: usize, ops: Vec, output: Vec, } impl GenericCPU { fn generic_process(&mut self) { let mut opcode; let mut operand; loop { opcode = match self.ops.get(self.pos) { None => break, Some(x) => *x, }; operand = *self.ops.get(self.pos + 1).unwrap(); match opcode { ADV => { debug!("truncating A({}) >> {} = {}", self.a, self.comboop(operand), self.a >> self.comboop(operand)); self.a >>= self.comboop(operand); }, BXL => { debug!("XOR B({}) with {} = {}", self.b, operand, self.b ^ operand); self.b ^= operand; }, BST => { self.b = self.comboop(operand) % 8; debug!("Setting B to comboop({}) = ({})", operand, self.b) }, JNZ => { if self.a != 0 { debug!("Jumping to {}", operand); self.pos = operand as usize; continue; } debug!("Do nothing"); }, BXC => { debug!("XORing B and C"); self.b ^= self.c; }, OUT => { debug!("printing {} % 8 = {}", self.comboop(operand), self.comboop(operand) % 8); self.output.push(self.comboop(operand) % 8); }, BDV => { debug!("truncating A({}) >> {} = B({})", self.a, self.comboop(operand), self.a >> self.comboop(operand)); self.b = self.a >> self.comboop(operand); }, CDV => { debug!("truncating A({}) >> {} = C({})", self.a, self.comboop(operand), self.a >> self.comboop(operand)); self.c = self.a >> self.comboop(operand); }, x => { debug!("opcode {x} unimplemented"); break; }, } self.pos += 2; } } fn comboop(&self, op: u64) -> u64 { match op { 0..=3 => op, 4 => self.a, 5 => self.b, 6 => self.c, 7 => unimplemented!(), _ => unreachable!(), } } fn reset(&mut self) { self.a = 0; self.b = 0; self.c = 0; self.pos = 0; self.output.clear(); } } fn get_content(i: &str) -> String { if i == "" { i.to_string() } else { i.split_once(':').unwrap().1.trim().to_string() } } fn round(n: u64) -> u64 { let mut b = n % 8; b ^= 6; b ^= n >> b; b ^= 7; b % 8 } fn main() { let data = convert_to_array::<_, _, '\n'>(DATA, get_content); let mut mem = GenericCPU { a: parsenumber(&data[0]), b: parsenumber(&data[1]), c: parsenumber(&data[2]), pos: 0, ops: data[4].split(',').map(|op| parsenumber(op)).collect(), output: Vec::new(), }; let mut n = mem.a; while n > 0 { println!("Round: {}", round(n)); n >>= 3; } mem.generic_process(); let output = mem.output.iter().map(|x| x.to_string()).collect::>().join(","); println!("output: {}", output); let mut n: u64 = 0; 'numloop: for (pos, num) in mem.ops.iter().enumerate().map(|(x, y)| (x * 3, *y)).rev() { for i in 0..64 { println!("Testing round({}) == {}", ((n >> pos) + i).separate_with_underscores(), num); if round((n >> pos) + i) == num { println!("Adding {}, the source of {}", i, num); n += i << pos; continue 'numloop; } } println!("Not Possible to find equivalent of {}", num); } println!("Testing {}", format!("{:018b}", n).separate_with_dots()); if n >= 48331947862051 { println!("Failed"); return; } mem.reset(); mem.a = n; mem.generic_process(); if mem.output == mem.ops { println!("A: {n}") } else { println!("{:?}\n{:?}", mem.ops, mem.output); } } #[cfg(test)] mod test { use super::*; }