The task today was to extend the interpreter written on day 2 with new opcodes.
Implementing the new opcodes was not difficult but handling the different modes for the parameter and overall design was a bit of work. Overall this was very interesting to work on and I hope there are more days where we get to work on this interpreter.
Code
struct VM {
memory: Vec<i32>,
ip: usize,
input: i32,
output: Vec<i32>,
}
impl From<&str> for VM {
fn from(input: &str) -> Self {
let memory = input
.trim()
.split(',')
.map(|x| x.parse().unwrap())
.collect();
VM {
memory,
ip: 0,
input: 0,
output: Vec::new(),
}
}
}
impl VM {
pub fn run(&mut self) {
loop {
if !self.run_cycle() {
break;
};
}
}
pub fn set_input(&mut self, input: i32) {
self.input = input;
}
pub fn get_output(&self) -> Vec<i32> {
self.output.clone()
}
fn get_param(&self, param: usize) -> i32 {
let opcode = self.memory[self.ip];
let power = 10i32.pow((param + 1) as u32);
let parameter_mode = (opcode / power) % 10;
match parameter_mode {
0 => self.memory[self.memory[self.ip + param] as usize],
1 => self.memory[self.ip + param],
_ => panic!("Unknown parameter mode: {}", parameter_mode),
}
}
fn get_address(&self, position: usize) -> usize {
self.memory[self.ip + position] as usize
}
fn op_add(&mut self) {
let addr = self.get_address(3);
self.memory[addr] = self.get_param(1) + self.get_param(2);
self.ip += 4
}
fn op_mul(&mut self) {
let addr = self.get_address(3);
self.memory[addr] = self.get_param(1) * self.get_param(2);
self.ip += 4
}
fn op_in(&mut self) {
let addr = self.get_address(1);
self.memory[addr] = self.input;
self.ip += 2;
}
fn op_out(&mut self) {
self.output.push(self.get_param(1));
self.ip += 2
}
fn op_jnz(&mut self) {
if self.get_param(1) != 0 {
self.ip = self.get_param(2) as usize;
} else {
self.ip += 3
}
}
fn op_jz(&mut self) {
if self.get_param(1) == 0 {
self.ip = self.get_param(2) as usize;
} else {
self.ip += 3
}
}
fn op_lt(&mut self) {
let addr = self.get_address(3);
if self.get_param(1) < self.get_param(2) {
self.memory[addr] = 1;
} else {
self.memory[addr] = 0;
}
self.ip += 4
}
fn op_eq(&mut self) {
let addr = self.get_address(3);
if self.get_param(1) == self.get_param(2) {
self.memory[addr] = 1;
} else {
self.memory[addr] = 0;
}
self.ip += 4
}
fn run_cycle(&mut self) -> bool {
let opcode = self.memory[self.ip] % 100;
match opcode {
1 => self.op_add(),
2 => self.op_mul(),
3 => self.op_in(),
4 => self.op_out(),
5 => self.op_jnz(),
6 => self.op_jz(),
7 => self.op_lt(),
8 => self.op_eq(),
99 => return false,
_ => panic!("Unknown opcode: {}", self.memory[self.ip]),
}
true
}
}
pub fn part_one(input: &str) -> i32 {
let mut vm = VM::from(input);
vm.set_input(1);
vm.run();
vm.get_output().pop().unwrap()
}
pub fn part_two(input: &str) -> i32 {
let mut vm = VM::from(input);
vm.set_input(5);
vm.run();
vm.get_output().pop().unwrap()
}
Benchmarks
2019 - Day 05 - Part 1 time: 22.788µs
2019 - Day 05 - Part 2 time: 18.652µs
Feedback or Questions?
I don't have a direct way to leave comments on posts. But if you want to give some feedback or have some questions you can contact in other ways.