Skip to main content

aoc/year2019/
intcode.rs

1//! Implementation of the full Intcode computer specification.
2use std::collections::VecDeque;
3
4/// [SWAG](https://en.wikipedia.org/wiki/Scientific_wild-ass_guess)
5/// It's possible that some inputs will need more space than this.
6/// At least one input for day 17 is known to produce a grid size of 85x61 in this region.
7const EXTRA: usize = 6_000;
8
9pub enum State {
10    Input,
11    Output(i64),
12    Halted,
13}
14
15pub struct Computer {
16    pc: usize,
17    base: usize,
18    code: Vec<usize>,
19    input: VecDeque<usize>,
20}
21
22impl Computer {
23    pub fn new(input: &[i64]) -> Computer {
24        let mut code = Vec::with_capacity(input.len() + EXTRA);
25        code.extend(input.iter().map(|&i| i as usize));
26        code.resize(code.len() + EXTRA, 0);
27
28        Computer { pc: 0, base: 0, code, input: VecDeque::new() }
29    }
30
31    pub fn input(&mut self, value: i64) {
32        self.input.push_back(value as usize);
33    }
34
35    pub fn input_ascii(&mut self, ascii: &str) {
36        self.input.extend(ascii.bytes().map(|b| b as usize));
37    }
38
39    /// Resets state *except* for memory which may have been modified.
40    pub fn reset(&mut self) {
41        self.pc = 0;
42        self.base = 0;
43        self.input.clear();
44    }
45
46    /// Runs until either the program needs input, outputs a value or encounters the halt opcode.
47    /// In the first two cases, the computer can be resumed by calling `run` again.
48    pub fn run(&mut self) -> State {
49        loop {
50            let op = self.code[self.pc];
51
52            match op % 100 {
53                // Add
54                1 => {
55                    let first = self.address(op / 100, 1);
56                    let second = self.address(op / 1000, 2);
57                    let third = self.address(op / 10000, 3);
58                    self.code[third] = self.code[first].wrapping_add(self.code[second]);
59                    self.pc += 4;
60                }
61                // Multiply
62                2 => {
63                    let first = self.address(op / 100, 1);
64                    let second = self.address(op / 1000, 2);
65                    let third = self.address(op / 10000, 3);
66                    self.code[third] = self.code[first].wrapping_mul(self.code[second]);
67                    self.pc += 4;
68                }
69                // Read input channel.
70                3 => {
71                    let Some(value) = self.input.pop_front() else {
72                        break State::Input;
73                    };
74                    let first = self.address(op / 100, 1);
75                    self.code[first] = value;
76                    self.pc += 2;
77                }
78                // Write output channel.
79                4 => {
80                    let first = self.address(op / 100, 1);
81                    let value = self.code[first];
82                    self.pc += 2;
83                    break State::Output(value as i64);
84                }
85                // Jump if true.
86                5 => {
87                    let first = self.address(op / 100, 1);
88                    let second = self.address(op / 1000, 2);
89                    let value = self.code[first] == 0;
90                    self.pc = if value { self.pc + 3 } else { self.code[second] };
91                }
92                // Jump if false.
93                6 => {
94                    let first = self.address(op / 100, 1);
95                    let second = self.address(op / 1000, 2);
96                    let value = self.code[first] == 0;
97                    self.pc = if value { self.code[second] } else { self.pc + 3 };
98                }
99                // Less than
100                7 => {
101                    let first = self.address(op / 100, 1);
102                    let second = self.address(op / 1000, 2);
103                    let third = self.address(op / 10000, 3);
104                    let value = (self.code[first] as i64) < (self.code[second] as i64);
105                    self.code[third] = value as usize;
106                    self.pc += 4;
107                }
108                // Equals
109                8 => {
110                    let first = self.address(op / 100, 1);
111                    let second = self.address(op / 1000, 2);
112                    let third = self.address(op / 10000, 3);
113                    let value = self.code[first] == self.code[second];
114                    self.code[third] = value as usize;
115                    self.pc += 4;
116                }
117                // Adjust relative base.
118                9 => {
119                    let first = self.address(op / 100, 1);
120                    self.base = self.base.wrapping_add(self.code[first]);
121                    self.pc += 2;
122                }
123                _ => break State::Halted,
124            }
125        }
126    }
127
128    /// Calculates an address using one of the three possible address modes.
129    #[inline]
130    fn address(&self, mode: usize, offset: usize) -> usize {
131        let index = self.pc + offset;
132        match mode % 10 {
133            0 => self.code[index],
134            1 => index,
135            2 => self.base.wrapping_add(self.code[index]),
136            _ => unreachable!(),
137        }
138    }
139}