|
| 1 | +use std::collections::{HashMap, VecDeque}; |
| 2 | +use std::fmt::{Display, Formatter, Result}; |
| 3 | +use std::fs; |
| 4 | +use regex::Regex; |
| 5 | + |
| 6 | +#[derive(Debug)] |
| 7 | +struct CPU { |
| 8 | + regs: HashMap<char, u32>, |
| 9 | + program: Vec<u32>, |
| 10 | + ip: usize, |
| 11 | + output: Vec<u32>, |
| 12 | +} |
| 13 | + |
| 14 | +impl CPU { |
| 15 | + fn new() -> CPU { |
| 16 | + CPU { |
| 17 | + regs: HashMap::new(), |
| 18 | + program: vec![], |
| 19 | + ip: 0, |
| 20 | + output: vec![], |
| 21 | + } |
| 22 | + } |
| 23 | + |
| 24 | + fn load_reg(&mut self, reg: &char, value: u32) { |
| 25 | + self.regs.insert(*reg, value); |
| 26 | + } |
| 27 | + |
| 28 | + fn load_program(&mut self, program: Vec<u32>) { |
| 29 | + self.program = program; |
| 30 | + } |
| 31 | + |
| 32 | + fn combo(&self, op: u32) -> u32 { |
| 33 | + match op { |
| 34 | + 0..=3 => op, |
| 35 | + 4 => self.regs[&'a'], |
| 36 | + 5 => self.regs[&'b'], |
| 37 | + 6 => self.regs[&'c'], |
| 38 | + _ => panic!("Illegal combo operator: {op}.") |
| 39 | + } |
| 40 | + } |
| 41 | + |
| 42 | + fn step(&mut self) -> bool { |
| 43 | + if self.ip >= self.program.len() { return false; } |
| 44 | + |
| 45 | + let insn = self.program[self.ip]; |
| 46 | + self.ip += 1; |
| 47 | + let op = self.program[self.ip]; |
| 48 | + self.ip += 1; |
| 49 | + |
| 50 | + match insn { |
| 51 | + 0 => { self.regs.insert('a', self.regs[&'a'] / 2_u32.pow(self.combo(op))); }, |
| 52 | + 1 => { self.regs.insert('b', self.regs[&'b'] ^ op); }, |
| 53 | + 2 => { self.regs.insert('b', self.combo(op) % 8); }, |
| 54 | + 3 => { if self.regs[&'a'] != 0 { self.ip = op as usize; }; }, |
| 55 | + 4 => { self.regs.insert('b', self.regs[&'b'] ^ self.regs[&'c']); }, |
| 56 | + 5 => { self.output.push(self.combo(op) % 8); }, |
| 57 | + 6 => { self.regs.insert('b', self.regs[&'a'] / 2_u32.pow(self.combo(op))); }, |
| 58 | + 7 => { self.regs.insert('c', self.regs[&'a'] / 2_u32.pow(self.combo(op))); }, |
| 59 | + _ => { panic!("Illegal opcode: {op}."); }, |
| 60 | + }; |
| 61 | + true |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +impl Display for CPU { |
| 66 | + fn fmt(&self, f: &mut Formatter<'_>) -> Result { |
| 67 | + write!(f, "<CPU:\n")?; |
| 68 | + for (reg, value) in self.regs.iter() { |
| 69 | + write!(f, "{}={}\n", reg, value)?; |
| 70 | + } |
| 71 | + write!(f, "IP={}", self.ip)?; |
| 72 | + write!(f, "\nProgram: ")?; |
| 73 | + for inst in self.program.iter() { |
| 74 | + write!(f, "{} ", inst)?; |
| 75 | + } |
| 76 | + write!(f, "\nOutput: ")?; |
| 77 | + for out in self.output.iter() { |
| 78 | + write!(f, "{} ", out)?; |
| 79 | + } |
| 80 | + write!(f, "\n>")?; |
| 81 | + Ok(()) |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +fn main() { |
| 86 | + let input = fs::read_to_string("../17.input").unwrap(); |
| 87 | + |
| 88 | + let reg_format = Regex::new(r"^Register (?<reg>[[:alpha:]]): (?<val>[0-9]+)$").unwrap(); |
| 89 | + let prog_format = Regex::new(r"^Program: (?<insns>[,0-9]+)$").unwrap(); |
| 90 | + |
| 91 | + let mut cpu = CPU::new(); |
| 92 | + |
| 93 | + let mut lines: VecDeque<&str> = VecDeque::from_iter(input.lines()); |
| 94 | + loop { |
| 95 | + let line = lines.pop_front() |
| 96 | + .unwrap() |
| 97 | + .trim(); |
| 98 | + if line.is_empty() { break; } |
| 99 | + let caps = reg_format.captures(line).unwrap(); |
| 100 | + cpu.load_reg(&caps["reg"].to_lowercase().chars().nth(0).unwrap(), |
| 101 | + (&caps["val"]).parse::<u32>().unwrap()); |
| 102 | + } |
| 103 | + let line = lines.pop_front().unwrap().trim(); |
| 104 | + let caps = prog_format.captures(line).unwrap(); |
| 105 | + cpu.load_program((&caps["insns"]) |
| 106 | + .split(",") |
| 107 | + .map(|i| i.parse::<u32>().unwrap()) |
| 108 | + .collect()); |
| 109 | + |
| 110 | + while cpu.step() {} |
| 111 | + println!("{}", cpu.output.iter().map(|o| o.to_string()).collect::<Vec<_>>().join(",")); |
| 112 | +} |
0 commit comments