Skip to main content

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