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