advent_of_code/year2019/
int_code.rs1use std::collections::VecDeque;
2
3pub type Word = i64;
4
5#[derive(Clone)]
6pub struct Program {
7 memory: Vec<Word>,
8 instruction_pointer: usize,
9 output_values: Vec<Word>,
10 input_values: VecDeque<Word>,
11 halted: bool,
12 requires_input_to: Option<usize>,
13 relative_base: Word,
14}
15
16enum Parameter {
17 Value(Word),
18 Address(usize),
19}
20
21impl Program {
22 pub fn parse(input: &str) -> Result<Self, String> {
23 let mut memory: Vec<Word> = Vec::new();
24 for word_string in input.trim().split(',') {
25 match word_string.parse::<Word>() {
26 Ok(value) => {
27 memory.push(value);
28 }
29 Err(error) => {
30 return Err(format!("Unable to parse program word ({error})"));
31 }
32 }
33 }
34 Ok(Self {
35 memory,
36 instruction_pointer: 0,
37 output_values: Vec::new(),
38 input_values: VecDeque::new(),
39 halted: false,
40 requires_input_to: None,
41 relative_base: 0,
42 })
43 }
44
45 pub const fn is_halted(&self) -> bool {
46 self.halted
47 }
48
49 pub fn run_until_halt_or_input(&mut self, max_instructions: u32) -> Result<(), String> {
50 if self.requires_input_to.is_some() {
51 return Err("Cannot run program requiring input".to_string());
52 } else if self.halted {
53 return Err("Cannot run halted program".to_string());
54 }
55
56 let mut current_instruction = 0;
57 while !self.halted && self.requires_input_to.is_none() {
58 self.evaluate()?;
59
60 current_instruction += 1;
61 if current_instruction == max_instructions {
62 return Err(format!("Aborted after {max_instructions} instructions"));
63 }
64 }
65 Ok(())
66 }
67
68 pub fn run_for_output(&mut self) -> Result<Vec<Word>, String> {
69 self.run_until_halt_or_input(1_000_000_000)?;
70 Ok(std::mem::take(&mut self.output_values))
71 }
72
73 pub fn run_for_output_limited(&mut self, max_instructions: u32) -> Result<Vec<Word>, String> {
74 self.run_until_halt_or_input(max_instructions)?;
75 Ok(std::mem::take(&mut self.output_values))
76 }
77
78 pub fn input(&mut self, input_value: Word) {
79 if let Some(save_address) = self.requires_input_to {
80 self.write_memory(save_address, input_value);
81 self.requires_input_to = None;
82 } else {
83 self.input_values.push_back(input_value);
84 }
85 }
86
87 pub fn input_string(&mut self, input_string: &str) {
88 input_string.bytes().for_each(|c| {
89 self.input(Word::from(c));
90 });
91 }
92
93 fn parameter_mode(
94 &self,
95 instruction: Word,
96 parameter_position: u32,
97 ) -> Result<Parameter, String> {
98 let parameter = self.read_memory(self.instruction_pointer + parameter_position as usize);
99 let divider = 10_i64.pow(parameter_position + 1);
100 let mode = ((instruction / divider) % 10) as u8;
101 let address = match mode {
102 1 => {
103 return Ok(Parameter::Value(parameter));
104 }
105 2 => parameter + self.relative_base,
106 _ => parameter,
107 };
108 if !(0..=10_000).contains(&address) {
109 return Err(format!("Bad address: {address}"));
110 }
111 Ok(Parameter::Address(address as usize))
112 }
113
114 fn output_location(&self, instruction: Word, parameter_position: u32) -> Result<usize, String> {
115 if let Parameter::Address(location) =
116 self.parameter_mode(instruction, parameter_position)?
117 {
118 return Ok(location);
119 }
120 Err("Invalid parameter mode for where to write".to_string())
121 }
122
123 fn parameter_value(&self, instruction: Word, parameter_position: u32) -> Result<Word, String> {
124 Ok(
125 match self.parameter_mode(instruction, parameter_position)? {
126 Parameter::Value(value) => value,
127 Parameter::Address(location) => self.read_memory(location),
128 },
129 )
130 }
131
132 fn evaluate(&mut self) -> Result<(), String> {
133 let instruction = self.read_memory(self.instruction_pointer);
134 let opcode = instruction % 100;
135
136 match opcode {
137 1 | 2 => {
138 let parameter1 = self.parameter_value(instruction, 1)?;
139 let parameter2 = self.parameter_value(instruction, 2)?;
140 let output_location = self.output_location(instruction, 3)?;
141 let value = if opcode == 1 {
142 parameter1.checked_add(parameter2)
143 } else {
144 parameter1.checked_mul(parameter2)
145 }
146 .ok_or("Overflow in program")?;
147
148 self.write_memory(output_location, value);
149 self.instruction_pointer += 4;
150 }
151 3 => {
152 let output_location = self.output_location(instruction, 1)?;
154 if let Some(input_value) = self.input_values.pop_front() {
155 self.write_memory(output_location, input_value);
156 } else {
157 self.requires_input_to = Some(output_location);
158 }
159 self.instruction_pointer += 2;
160 }
161 4 => {
162 self.output_values
164 .push(self.parameter_value(instruction, 1)?);
165 self.instruction_pointer += 2;
166 }
167 5 | 6 => {
168 let jump_if = opcode == 5;
173 let parameter_1_true = self.parameter_value(instruction, 1)? != 0;
174 if parameter_1_true == jump_if {
175 self.instruction_pointer = self.parameter_value(instruction, 2)? as usize;
176 } else {
177 self.instruction_pointer += 3;
178 }
179 }
180 7 | 8 => {
181 let parameter_1 = self.parameter_value(instruction, 1);
186 let parameter_2 = self.parameter_value(instruction, 2);
187 let output_value = i64::from(
188 (opcode == 7 && (parameter_1 < parameter_2))
189 || (opcode == 8 && (parameter_1 == parameter_2)),
190 );
191
192 let output_location = self.output_location(instruction, 3)?;
193 self.write_memory(output_location, output_value);
194 self.instruction_pointer += 4;
195 }
196 9 => {
197 self.relative_base += self.parameter_value(instruction, 1)?;
198 self.instruction_pointer += 2;
199 }
200 99 => {
201 self.halted = true;
202 }
203 _ => {
204 return Err(format!("Invalid opcode: {opcode}"));
205 }
206 }
207
208 Ok(())
209 }
210
211 pub fn read_memory(&self, address: usize) -> Word {
212 *self.memory.get(address).unwrap_or(&0_i64)
213 }
214
215 pub fn write_memory(&mut self, address: usize, value: Word) {
216 if self.memory.len() <= address {
217 self.memory.resize(address + 1, 0);
218 }
219 self.memory[address] = value;
220 }
221}