Advent of Code 2019 - Day 5

Advent of CodeRust

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.

©2019–2020 Severin Kaderli