use std::collections::HashMap; use std::path::Path; pub type RAM = HashMap; #[allow(dead_code)] pub fn run() { let input = std::fs::read_to_string("input/day05.txt").unwrap(); let ram: RAM = input .split(",") .enumerate() .map(|(i, s)| (i, s.parse::().unwrap())) .collect(); task1(ram.clone()); task2(ram.clone()); } pub fn load_ram>(path: P) -> RAM { let input = std::fs::read_to_string(path).unwrap(); input .split(",") .enumerate() .map(|(i, s)| (i, s.parse::().unwrap())) .collect() } pub struct IntCodeComputer { input_storage: Vec, output_storage: Vec, pc: usize, ram: RAM, relative_base_offset: i128, } #[derive(PartialEq, Debug)] enum OpCode { Add, Multiply, Input, Output, JumpIfTrue, JumpIfFalse, LessThan, Equals, AdjustRelBase, Terminate, } #[derive(Debug)] enum OpParam { Immediate(i128), Memory(usize), Relative(i128), } impl OpParam { fn from(val: i128, type_number: i128) -> Self { match type_number { 0 => OpParam::Memory(val as usize), 1 => OpParam::Immediate(val), 2 => OpParam::Relative(val), _ => unreachable!("unexpected parameter"), } } fn retrieve(&self, memory: &RAM, relative_base_offset: i128) -> i128 { match self { OpParam::Immediate(v) => *v, OpParam::Memory(i) => memory.get_default(*i), OpParam::Relative(i) => memory.get_default((relative_base_offset + *i) as usize), } } fn store(&self, memory: &mut RAM, value: i128, relative_base_offset: i128) { match self { OpParam::Immediate(_) => panic!("cannot save to immediate position"), OpParam::Memory(i) => { memory.insert(*i, value); } OpParam::Relative(i) => { memory.insert((relative_base_offset + *i) as usize, value); } } } } #[derive(Debug)] struct Op { opcode: OpCode, params: Vec, } impl Op { fn from(pc: usize, memory: &RAM) -> Self { //opcode: 123xx -> 1, 2, 3: parameter type for parameters 1-3, xx = actual op code let mut opcode = memory.get_default(pc); let p_type1 = opcode / 10_000; opcode %= 10_000; let p_type2 = opcode / 1000; opcode %= 1000; let p_type3 = opcode / 100; opcode %= 100; match opcode { 1 => Op { opcode: OpCode::Add, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), OpParam::from(memory.get_default(pc + 3), p_type1), ], }, 2 => Op { opcode: OpCode::Multiply, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), OpParam::from(memory.get_default(pc + 3), p_type1), ], }, 3 => Op { opcode: OpCode::Input, params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)], }, 4 => Op { opcode: OpCode::Output, params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)], }, 5 => Op { opcode: OpCode::JumpIfTrue, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), ], }, 6 => Op { opcode: OpCode::JumpIfFalse, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), ], }, 7 => Op { opcode: OpCode::LessThan, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), OpParam::from(memory.get_default(pc + 3), p_type1), ], }, 8 => Op { opcode: OpCode::Equals, params: vec![ OpParam::from(memory.get_default(pc + 1), p_type3), OpParam::from(memory.get_default(pc + 2), p_type2), OpParam::from(memory.get_default(pc + 3), p_type1), ], }, 9 => Op { opcode: OpCode::AdjustRelBase, params: vec![OpParam::from(memory.get_default(pc + 1), p_type3)], }, 99 => Op { opcode: OpCode::Terminate, params: vec![], }, _ => unreachable!("unknown op code {}", opcode), } } } impl IntCodeComputer { pub fn new(input: Vec, memory: RAM) -> Self { IntCodeComputer { input_storage: input, output_storage: Vec::new(), pc: 0, ram: memory, relative_base_offset: 0, } } pub fn run_single(&mut self, input: i128) -> i128 { self.set_input(&[input]); self.clear_output(); self.run_until_input_empty(); *self.get_output().first().unwrap() } pub fn run_until_end(&mut self) { if !self.run_until_input_empty() { panic!("There wasn't enough input given to run until the program halted"); } } /** * True means the program terminated, false means it is just waiting for more input */ pub fn run_until_input_empty(&mut self) -> bool { let mut op = Op::from(self.pc, &self.ram); let mut inputs = self.input_storage.iter(); while op.opcode != OpCode::Terminate { match op.opcode { OpCode::Add => { let r = op.params[0].retrieve(&self.ram, self.relative_base_offset) + op.params[1].retrieve(&self.ram, self.relative_base_offset); op.params[2].store(&mut self.ram, r, self.relative_base_offset); self.pc += 4; } OpCode::Multiply => { let r = op.params[0].retrieve(&self.ram, self.relative_base_offset) * op.params[1].retrieve(&self.ram, self.relative_base_offset); op.params[2].store(&mut self.ram, r, self.relative_base_offset); self.pc += 4; } OpCode::Input => { if let Some(v) = inputs.next() { op.params[0].store(&mut self.ram, *v, self.relative_base_offset); self.pc += 2; } else { self.input_storage.clear(); return false; } } OpCode::Output => { self.output_storage .push(op.params[0].retrieve(&self.ram, self.relative_base_offset)); self.pc += 2; } OpCode::JumpIfTrue => { if op.params[0].retrieve(&self.ram, self.relative_base_offset) > 0 { self.pc = op.params[1].retrieve(&self.ram, self.relative_base_offset) as usize; } else { self.pc += 3; } } OpCode::JumpIfFalse => { if op.params[0].retrieve(&self.ram, self.relative_base_offset) == 0 { self.pc = op.params[1].retrieve(&self.ram, self.relative_base_offset) as usize; } else { self.pc += 3; } } OpCode::LessThan => { let r = if op.params[0].retrieve(&self.ram, self.relative_base_offset) < op.params[1].retrieve(&self.ram, self.relative_base_offset) { 1 } else { 0 }; op.params[2].store(&mut self.ram, r, self.relative_base_offset); self.pc += 4; } OpCode::Equals => { let r = if op.params[0].retrieve(&self.ram, self.relative_base_offset) == op.params[1].retrieve(&self.ram, self.relative_base_offset) { 1 } else { 0 }; op.params[2].store(&mut self.ram, r, self.relative_base_offset); self.pc += 4; } OpCode::AdjustRelBase => { self.relative_base_offset += op.params[0].retrieve(&self.ram, self.relative_base_offset); self.pc += 2; } OpCode::Terminate => {} } op = Op::from(self.pc, &self.ram); } return true; } pub fn set_input(&mut self, input: &[i128]) { self.input_storage.clear(); for x in input { self.input_storage.push(*x); } } pub fn add_input(&mut self, input: &[i128]) { for x in input { self.input_storage.push(*x); } } pub fn get_input(&self) -> &[i128] { self.input_storage.as_slice() } pub fn clear_output(&mut self) { self.output_storage.clear(); } pub fn clear_n_output(&mut self, n: usize) { self.output_storage = Vec::from(self.output_storage.split_at(n).1); } pub fn get_output(&self) -> &[i128] { self.output_storage.as_slice() } } fn task1(ram: RAM) { let mut computer = IntCodeComputer::new(vec![1], ram); computer.run_until_end(); println!("{:?}", computer.get_output()); println!("Task 1: {}", computer.get_output().last().unwrap()); } fn task2(ram: RAM) { let mut computer = IntCodeComputer::new(vec![5], ram); computer.run_until_end(); println!("{:?}", computer.get_output()); println!("Task 2: {}", computer.get_output().last().unwrap()); } trait Easy { fn get_default(&self, index: usize) -> i128; } impl Easy for RAM { fn get_default(&self, index: usize) -> i128 { **self.get(&index).get_or_insert(&0) } }