|
1 | 1 | //! # Sensor Boost
|
2 | 2 | //!
|
3 |
| -//! This problem is essentially an unit test for the canonical full intcode computer |
4 |
| -//! used heavily by other days. |
| 3 | +//! This problem is essentially a unit test for the full intcode computer used by other days. |
5 | 4 | use crate::util::parse::*;
|
6 | 5 | use intcode::*;
|
7 | 6 |
|
8 | 7 | pub mod intcode {
|
9 |
| - use std::sync::mpsc::*; |
10 |
| - use std::thread; |
| 8 | + use std::collections::VecDeque; |
| 9 | + |
| 10 | + pub enum State { |
| 11 | + Input, |
| 12 | + Output(i64), |
| 13 | + Halted, |
| 14 | + } |
11 | 15 |
|
12 | 16 | pub struct Computer {
|
13 | 17 | pc: usize,
|
14 | 18 | base: i64,
|
15 | 19 | code: Vec<i64>,
|
16 |
| - input_rx: Receiver<i64>, |
17 |
| - output_tx: Sender<i64>, |
| 20 | + input: VecDeque<i64>, |
18 | 21 | }
|
19 | 22 |
|
20 | 23 | impl Computer {
|
21 |
| - /// Spawns an `IntCode` computer in a new thread, returning an input and output channel |
22 |
| - /// for communicating asynchronously with the computer via the opcodes 3 and 4. |
23 |
| - pub fn spawn(code: &[i64]) -> (Sender<i64>, Receiver<i64>) { |
24 |
| - let pc = 0; |
25 |
| - let base = 0; |
26 |
| - let code = code.to_vec(); |
27 |
| - let (input_tx, input_rx) = channel(); |
28 |
| - let (output_tx, output_rx) = channel(); |
29 |
| - |
30 |
| - let mut computer = Computer { pc, base, code, input_rx, output_tx }; |
31 |
| - thread::spawn(move || computer.run()); |
| 24 | + pub fn new(code: &[i64]) -> Computer { |
| 25 | + Computer { pc: 0, base: 0, code: code.to_vec(), input: VecDeque::new() } |
| 26 | + } |
32 | 27 |
|
33 |
| - (input_tx, output_rx) |
| 28 | + pub fn input(&mut self, slice: &[i64]) { |
| 29 | + self.input.extend(slice); |
34 | 30 | }
|
35 | 31 |
|
36 |
| - /// Runs until a `99` opcode instruction is encountered. |
37 |
| - fn run(&mut self) { |
| 32 | + /// Runs until either the program needs input, outputs a value or encounters the halt |
| 33 | + /// opcode. In the first two cases, the computer can be restarted by calling `run` again. |
| 34 | + pub fn run(&mut self) -> State { |
38 | 35 | loop {
|
39 |
| - match self.code[self.pc] % 100 { |
| 36 | + let code = self.code[self.pc]; |
| 37 | + |
| 38 | + match code % 100 { |
40 | 39 | // Add
|
41 | 40 | 1 => {
|
42 |
| - let value = self.read(1) + self.read(2); |
43 |
| - self.write(3, value); |
| 41 | + let first = self.address(code / 100, 1); |
| 42 | + let second = self.address(code / 1000, 2); |
| 43 | + let third = self.address(code / 10000, 3); |
| 44 | + self.code[third] = self.code[first] + self.code[second]; |
44 | 45 | self.pc += 4;
|
45 | 46 | }
|
46 | 47 | // Multiply
|
47 | 48 | 2 => {
|
48 |
| - let value = self.read(1) * self.read(2); |
49 |
| - self.write(3, value); |
| 49 | + let first = self.address(code / 100, 1); |
| 50 | + let second = self.address(code / 1000, 2); |
| 51 | + let third = self.address(code / 10000, 3); |
| 52 | + self.code[third] = self.code[first] * self.code[second]; |
50 | 53 | self.pc += 4;
|
51 | 54 | }
|
52 | 55 | // Read input channel
|
53 | 56 | 3 => {
|
54 |
| - let value = self.input_rx.recv().unwrap(); |
55 |
| - self.write(1, value); |
| 57 | + let Some(value) = self.input.pop_front() else { |
| 58 | + break State::Input; |
| 59 | + }; |
| 60 | + let first = self.address(code / 100, 1); |
| 61 | + self.code[first] = value; |
56 | 62 | self.pc += 2;
|
57 | 63 | }
|
58 | 64 | // Write output channel
|
59 | 65 | 4 => {
|
60 |
| - let value = self.read(1); |
61 |
| - let _ = self.output_tx.send(value); |
| 66 | + let first = self.address(code / 100, 1); |
| 67 | + let value = self.code[first]; |
62 | 68 | self.pc += 2;
|
| 69 | + break State::Output(value); |
63 | 70 | }
|
64 | 71 | // Jump if true
|
65 | 72 | 5 => {
|
66 |
| - let first = self.read(1); |
67 |
| - let second = self.read(2); |
68 |
| - self.pc = if first == 0 { self.pc + 3 } else { second as usize }; |
| 73 | + let first = self.address(code / 100, 1); |
| 74 | + let second = self.address(code / 1000, 2); |
| 75 | + let value = self.code[first] == 0; |
| 76 | + self.pc = if value { self.pc + 3 } else { self.code[second] as usize }; |
69 | 77 | }
|
70 | 78 | // Jump if false
|
71 | 79 | 6 => {
|
72 |
| - let first = self.read(1); |
73 |
| - let second = self.read(2); |
74 |
| - self.pc = if first == 0 { second as usize } else { self.pc + 3 }; |
| 80 | + let first = self.address(code / 100, 1); |
| 81 | + let second = self.address(code / 1000, 2); |
| 82 | + let value = self.code[first] == 0; |
| 83 | + self.pc = if value { self.code[second] as usize } else { self.pc + 3 }; |
75 | 84 | }
|
76 | 85 | // Less than
|
77 | 86 | 7 => {
|
78 |
| - let value = self.read(1) < self.read(2); |
79 |
| - self.write(3, value as i64); |
| 87 | + let first = self.address(code / 100, 1); |
| 88 | + let second = self.address(code / 1000, 2); |
| 89 | + let third = self.address(code / 10000, 3); |
| 90 | + let value = self.code[first] < self.code[second]; |
| 91 | + self.code[third] = value as i64; |
80 | 92 | self.pc += 4;
|
81 | 93 | }
|
82 | 94 | // Equals
|
83 | 95 | 8 => {
|
84 |
| - let value = self.read(1) == self.read(2); |
85 |
| - self.write(3, value as i64); |
| 96 | + let first = self.address(code / 100, 1); |
| 97 | + let second = self.address(code / 1000, 2); |
| 98 | + let third = self.address(code / 10000, 3); |
| 99 | + let value = self.code[first] == self.code[second]; |
| 100 | + self.code[third] = value as i64; |
86 | 101 | self.pc += 4;
|
87 | 102 | }
|
88 | 103 | // Adjust relative base
|
89 | 104 | 9 => {
|
90 |
| - let value = self.read(1); |
91 |
| - self.base += value; |
| 105 | + let first = self.address(code / 100, 1); |
| 106 | + self.base += self.code[first]; |
92 | 107 | self.pc += 2;
|
93 | 108 | }
|
94 |
| - _ => break, |
| 109 | + _ => break State::Halted, |
95 | 110 | }
|
96 | 111 | }
|
97 | 112 | }
|
98 | 113 |
|
99 |
| - /// Convenience wrapper for reading a value |
100 |
| - fn read(&mut self, offset: usize) -> i64 { |
101 |
| - let index = self.address(offset); |
102 |
| - self.code[index] |
103 |
| - } |
104 |
| - |
105 |
| - /// Convenience wrapper for writing a value |
106 |
| - fn write(&mut self, offset: usize, value: i64) { |
107 |
| - let index = self.address(offset); |
108 |
| - self.code[index] = value; |
109 |
| - } |
110 |
| - |
111 | 114 | /// Calculates an address using one of the three possible address modes.
|
112 | 115 | /// If the address exceeds the size of the `code` vector then it is extended with 0 values.
|
113 |
| - fn address(&mut self, offset: usize) -> usize { |
114 |
| - const FACTOR: [i64; 4] = [0, 100, 1000, 10000]; |
115 |
| - let mode = self.code[self.pc] / FACTOR[offset]; |
116 |
| - |
| 116 | + #[inline] |
| 117 | + fn address(&mut self, mode: i64, offset: usize) -> usize { |
117 | 118 | let index = match mode % 10 {
|
118 | 119 | 0 => self.code[self.pc + offset] as usize,
|
119 | 120 | 1 => self.pc + offset,
|
@@ -143,7 +144,10 @@ pub fn part2(input: &[i64]) -> i64 {
|
143 | 144 | }
|
144 | 145 |
|
145 | 146 | fn run(input: &[i64], value: i64) -> i64 {
|
146 |
| - let (tx, rx) = Computer::spawn(input); |
147 |
| - let _ = tx.send(value); |
148 |
| - rx.recv().unwrap() |
| 147 | + let mut computer = Computer::new(input); |
| 148 | + computer.input(&[value]); |
| 149 | + match computer.run() { |
| 150 | + State::Output(result) => result, |
| 151 | + _ => unreachable!(), |
| 152 | + } |
149 | 153 | }
|
0 commit comments