const OP_ADD: CELL = 1; const OP_MUL: CELL = 2; const OP_INPUT: CELL = 3; const OP_OUTPUT: CELL = 4; const OP_JUMP_IF_TRUE: CELL = 5; const OP_JUMP_IF_FALSE: CELL = 6; const OP_LESS_THAN: CELL = 7; const OP_EQUAL: CELL = 8; const OP_HALT: CELL = 99; pub type CELL = i64; type Mode = u8; #[derive(PartialEq, Eq, Clone, Debug)] pub enum SimulateStep { Finished, WaitForInput, Output(CELL), } impl SimulateStep { pub fn expect_output(self) -> CELL { if let Self::Output(v) = self { return v; } panic!("Expected Output, got {:?}", self); } pub fn expect_finished(self) { if let Self::Finished = self { return; } panic!("Expected Finished, got {:?}", self); } pub fn expect_wait_for_input(self) { if let Self::WaitForInput = self { return; } panic!("Expected WaitForInput, got {:?}", self); } } #[derive(PartialEq, Eq, Clone, Debug)] pub struct IntCode { pub ip: usize, pub next_input: Option, pub data: Vec, } impl IntCode { #[inline(always)] fn fetch(&mut self, mode: Mode) -> CELL { let imm = self.data[self.ip]; self.ip += 1; match mode { 1 => imm, _ => self.data[imm as usize], } } #[inline(always)] fn store(&mut self, mode: Mode, value: CELL) { let addr = self.ip; self.ip += 1; match mode { 1 => self.data[addr] = value, _ => { let addr = self.data[addr] as usize; self.data[addr] = value; }, } } fn binop(&mut self, op: F, modes: [Mode; 3]) where F: FnOnce(CELL, CELL) -> CELL, { let src1 = self.fetch(modes[0]); let src2 = self.fetch(modes[1]); self.store(modes[2], op(src1, src2)); } pub fn simulate(&mut self) { assert_eq!(self.ip, 0); assert!(self.next_input.is_none()); self.simulate_step().expect_finished(); } pub fn simulate_step(&mut self) -> SimulateStep { loop { let modes = self.data[self.ip]; self.ip += 1; let op = modes % 100; let modes = modes / 100; let mode1 = (modes % 10) as Mode; let modes = modes / 10; let mode2 = (modes % 10) as Mode; let modes = modes / 10; let mode3 = (modes % 10) as Mode; let modes = [mode1, mode2, mode3]; match op { OP_ADD => self.binop(|a, b| a + b, modes), OP_MUL => self.binop(|a, b| a * b, modes), OP_LESS_THAN => self.binop(|a, b| if a < b { 1 } else { 0 }, modes), OP_EQUAL => self.binop(|a, b| if a == b { 1 } else { 0 }, modes), OP_INPUT => { if let Some(v) = self.next_input.take() { self.store(mode1, v); } else { self.ip -= 1; // go back to opcode return SimulateStep::WaitForInput; } }, OP_OUTPUT => { return SimulateStep::Output(self.fetch(mode1)); }, OP_HALT => { self.ip -= 1; // make re-running this step end up here again return SimulateStep::Finished; }, OP_JUMP_IF_TRUE => { if self.fetch(mode1) != 0 { self.ip = self.fetch(mode2) as usize; } else { self.ip += 1; // skip target } }, OP_JUMP_IF_FALSE => { if self.fetch(mode1) == 0 { self.ip = self.fetch(mode2) as usize; } else { self.ip += 1; // skip target } }, _ => panic!("invalid opcode: {}", op), } } } } impl std::str::FromStr for IntCode { type Err = ::Err; fn from_str(s: &str) -> Result { Ok(IntCode { ip: 0, next_input: None, data: s.split(',').map(|s| s.trim().parse::()).collect::>()?, }) } }