aoc/year2019/
intcode.rs

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