1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Implementation of the full Intcode computer specification.
use std::collections::VecDeque;

const EXTRA: usize = 2_000;

pub enum State {
    Input,
    Output(i64),
    Halted,
}

pub struct Computer {
    pc: usize,
    base: usize,
    code: Vec<usize>,
    input: VecDeque<usize>,
}

impl Computer {
    pub fn new(input: &[i64]) -> Computer {
        let mut code = Vec::with_capacity(input.len() + EXTRA);
        code.extend(input.iter().map(|&i| i as usize));
        code.resize(input.len() + EXTRA, 0);

        Computer { pc: 0, base: 0, code, input: VecDeque::new() }
    }

    pub fn input(&mut self, value: i64) {
        self.input.push_back(value as usize);
    }

    pub fn input_ascii(&mut self, ascii: &str) {
        self.input.extend(ascii.bytes().map(|b| b as usize));
    }

    /// Resets state *except* for memory which may have been modified.
    pub fn reset(&mut self) {
        self.pc = 0;
        self.base = 0;
        self.input.clear();
    }

    /// Runs until either the program needs input, outputs a value or encounters the halt opcode.
    /// In the first two cases, the computer can be resumed by calling `run` again.
    pub fn run(&mut self) -> State {
        loop {
            let op = self.code[self.pc];

            match op % 100 {
                // Add
                1 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let third = self.address(op / 10000, 3);
                    self.code[third] = self.code[first].wrapping_add(self.code[second]);
                    self.pc += 4;
                }
                // Multiply
                2 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let third = self.address(op / 10000, 3);
                    self.code[third] = self.code[first].wrapping_mul(self.code[second]);
                    self.pc += 4;
                }
                // Read input channel
                3 => {
                    let Some(value) = self.input.pop_front() else {
                        break State::Input;
                    };
                    let first = self.address(op / 100, 1);
                    self.code[first] = value;
                    self.pc += 2;
                }
                // Write output channel
                4 => {
                    let first = self.address(op / 100, 1);
                    let value = self.code[first];
                    self.pc += 2;
                    break State::Output(value as i64);
                }
                // Jump if true
                5 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let value = self.code[first] == 0;
                    self.pc = if value { self.pc + 3 } else { self.code[second] };
                }
                // Jump if false
                6 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let value = self.code[first] == 0;
                    self.pc = if value { self.code[second] } else { self.pc + 3 };
                }
                // Less than
                7 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let third = self.address(op / 10000, 3);
                    let value = (self.code[first] as i64) < (self.code[second] as i64);
                    self.code[third] = value as usize;
                    self.pc += 4;
                }
                // Equals
                8 => {
                    let first = self.address(op / 100, 1);
                    let second = self.address(op / 1000, 2);
                    let third = self.address(op / 10000, 3);
                    let value = self.code[first] == self.code[second];
                    self.code[third] = value as usize;
                    self.pc += 4;
                }
                // Adjust relative base
                9 => {
                    let first = self.address(op / 100, 1);
                    self.base = self.base.wrapping_add(self.code[first]);
                    self.pc += 2;
                }
                _ => break State::Halted,
            }
        }
    }

    /// Calculates an address using one of the three possible address modes.
    #[inline]
    fn address(&mut self, mode: usize, offset: usize) -> usize {
        match mode % 10 {
            0 => self.code[self.pc + offset],
            1 => self.pc + offset,
            2 => self.base.wrapping_add(self.code[self.pc + offset]),
            _ => unreachable!(),
        }
    }
}