Advent of Code 2019 - Day 9

Advent of CodeRust

Another day, another addition for the Intcode computer.

We only needed to handle a new parameter mode and a new opcode today. With my design of the computer these additions were pretty easy and it didn't take very long. Also according to the problem description this is the last addition to the Intcode computer but it will still be used for future problems.

Code

struct VM {
    memory: Vec<i128>,
    ip: usize,
    input: Vec<i128>,
    output: Vec<i128>,
    exit_code: ExitCode,
    relative_base: i128,
}

#[derive(Clone, PartialEq, Debug)]
enum ExitCode {
    Running,
    NeedInput,
    Stop,
}

impl From<&str> for VM {
    fn from(input: &str) -> Self {
        let mut memory: Vec<i128> = input
            .trim()
            .split(',')
            .map(|x| x.trim().parse().unwrap())
            .collect();
        memory.resize(4096, 0);

        VM {
            memory,
            ip: 0,
            input: Vec::new(),
            output: Vec::new(),
            exit_code: ExitCode::Running,
            relative_base: 0,
        }
    }
}

impl VM {
    pub fn run(&mut self) -> ExitCode {
        self.set_exit_code(ExitCode::Running);
        loop {
            if !self.run_cycle() {
                return self.get_exit_code();
            }
        }
    }

    fn get_exit_code(&self) -> ExitCode {
        self.exit_code.clone()
    }

    fn set_exit_code(&mut self, exit_code: ExitCode) {
        self.exit_code = exit_code;
    }

    pub fn add_input(&mut self, input: i128) {
        self.input.push(input);
    }

    pub fn get_output(&mut self) -> i128 {
        self.output.remove(0)
    }

    fn get_mode(&self, param: usize) -> i128 {
        let opcode = self.memory[self.ip];
        let power = 10i128.pow((param + 1) as u32);
        (opcode / power) % 10
    }

    fn get_param(&self, param: usize) -> i128 {
        let opcode = self.memory[self.ip];
        let power = 10i128.pow((param + 1) as u32);
        let parameter_mode = (opcode / power) % 10;

        match self.get_mode(param) {
            0 => self.memory[self.memory[self.ip + param] as usize],
            1 => self.memory[self.ip + param],
            2 => {
                let addr = self.memory[self.ip + param] + self.relative_base;
                self.memory[addr as usize]
            }
            _ => panic!("Unknown parameter mode: {}", parameter_mode),
        }
    }

    fn get_address(&self, position: usize) -> usize {
        match self.get_mode(position) {
            2 => (self.memory[self.ip + position] + self.relative_base) as 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) {
        if self.input.is_empty() {
            self.exit_code = ExitCode::NeedInput;
            return;
        }

        let addr = self.get_address(1);
        self.memory[addr] = self.input.remove(0);
        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 op_rel(&mut self) {
        self.relative_base += self.get_param(1);
        self.ip += 2
    }

    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(),
            9 => self.op_rel(),
            99 => self.set_exit_code(ExitCode::Stop),
            _ => panic!("Unknown opcode: {}", self.memory[self.ip]),
        }

        if self.exit_code != ExitCode::Running {
            return false;
        }

        true
    }
}

pub fn part_one(input: &str) -> i128 {
    let mut vm = VM::from(input);
    vm.add_input(1);
    vm.run();
    vm.get_output()
}

pub fn part_two(input: &str) -> i128 {
    let mut vm = VM::from(input);
    vm.add_input(2);
    vm.run();
    vm.get_output()
}

Benchmarks

2019 - Day 09 - Part 1 time: 858.374µs
2019 - Day 09 - Part 2 time: 114.812102ms

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