diff options
author | Justin Wernick <justin@worthe-it.co.za> | 2019-12-14 20:59:58 +0200 |
---|---|---|
committer | Justin Wernick <justin@worthe-it.co.za> | 2019-12-14 20:59:58 +0200 |
commit | 5c60610b768b98113ca8ca5e8f833fa42d5aa4cf (patch) | |
tree | 9c4573be2e1d6a626152880e568085d62d3340aa | |
parent | 754a9a4e220b039770b12cb0803b0b0ad3133555 (diff) |
Intcode computer to use bigints and unlimited memory space!
Also started a more complete error reporting scheme.
-rw-r--r-- | Cargo.lock | 84 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | src/bin/day_2.rs | 17 | ||||
-rw-r--r-- | src/bin/day_7.rs | 24 | ||||
-rw-r--r-- | src/lib.rs | 210 |
5 files changed, 238 insertions, 100 deletions
@@ -15,6 +15,7 @@ dependencies = [ "archery 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "derive_more 0.99.2 (registry+https://github.com/rust-lang/crates.io-index)", "im 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rpds 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -37,6 +38,11 @@ dependencies = [ ] [[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -100,6 +106,76 @@ version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "num" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-bigint" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-complex" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-iter" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-rational" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "proc-macro-error" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -257,6 +333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum archery 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d308d8fa3f687f7a7588fccc4812ed6914be09518232f00454693a7417273ad2" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum bitmaps 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81e039a80914325b37fde728ef7693c212f0ac913d5599607d7b95a9484aae0b" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" @@ -264,6 +341,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum im 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a1f9b6540e530defef7f2df4ed330d45b739b10450548c74a9913f63ea1acc6b" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +"checksum num 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf4825417e1e1406b3782a8ce92f4d53f26ec055e3622e1881ca8e9f5f9e08db" +"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" +"checksum num-complex 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" +"checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" +"checksum num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" "checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" @@ -9,4 +9,5 @@ structopt = "0.3.5" derive_more = "0.99.2" im = "14.0.0" rpds = "0.7.0" -archery = "0.3.0"
\ No newline at end of file +archery = "0.3.0" +num = "0.2"
\ No newline at end of file diff --git a/src/bin/day_2.rs b/src/bin/day_2.rs index ce8fcaa..ba9e189 100644 --- a/src/bin/day_2.rs +++ b/src/bin/day_2.rs @@ -41,7 +41,9 @@ fn main() { match (opt.noun, opt.verb, opt.output) { (Some(noun), Some(verb), _) => { let result = exit_on_failed_assertion( - program.with_noun_verb_input(noun, verb).execute(), + program + .with_noun_verb_input(noun, verb) + .execute_returning_memory_0(), "Program errored", ); println!("{}", result); @@ -52,7 +54,8 @@ fn main() { println!("({}, {})", noun, verb); } (None, None, None) => { - let result = exit_on_failed_assertion(program.execute(), "Program errored"); + let result = + exit_on_failed_assertion(program.execute_returning_memory_0(), "Program errored"); println!("{}", result); } _ => { @@ -77,17 +80,17 @@ fn find_input( output: Intcode, ) -> Result<(Intcode, Intcode), IntcodeProgramError> { (0..99) - .flat_map(|noun| (0..99).map(move |verb| (noun, verb))) + .flat_map(|noun| (0..99).map(move |verb| (Intcode::from(noun), Intcode::from(verb)))) .map(|(noun, verb)| { ( - noun, - verb, + noun.clone(), + verb.clone(), program .with_noun_verb_input(noun, verb) .execute_returning_memory_0(), ) }) - .find(|(_noun, _verb, out)| *out == Ok(Some(output))) + .find(|(_noun, _verb, out)| *out == Ok(output.clone())) .map(|(noun, verb, _out)| Ok((noun, verb))) - .unwrap_or(Err(IntcodeProgramError)) + .unwrap_or(Err(IntcodeProgramError::Unknown)) } diff --git a/src/bin/day_7.rs b/src/bin/day_7.rs index 0abf215..9b9177a 100644 --- a/src/bin/day_7.rs +++ b/src/bin/day_7.rs @@ -52,12 +52,12 @@ fn find_max_power( feedback_loop_mode: bool, ) -> Result<Intcode, IntcodeProgramError> { PhaseSetting::all(feedback_loop_mode) - .map(|phase| AmplifierArray::new(program, phase).execute()) + .map(|phase| AmplifierArray::new(program, &phase).execute()) .collect::<Result<Vec<Intcode>, _>>() - .map(|powers| powers.into_iter().max().unwrap_or(0)) + .map(|powers| powers.into_iter().max().unwrap_or(Intcode::from(0))) } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] struct PhaseSetting([Intcode; 5]); impl PhaseSetting { @@ -69,7 +69,7 @@ impl PhaseSetting { } } - fn permute(min: Intcode, max: Intcode) -> impl Iterator<Item = PhaseSetting> { + fn permute(min: i32, max: i32) -> impl Iterator<Item = PhaseSetting> { // This is an absolutely atrocious way to do the permutation, // but luckily it's only 5 elements long. (min..max) @@ -77,12 +77,14 @@ impl PhaseSetting { (min..max).flat_map(move |b| { (min..max).flat_map(move |c| { (min..max).flat_map(move |d| { - (min..max).map(move |e| PhaseSetting([a, b, c, d, e])) + (min..max).map(move |e| { + PhaseSetting([a.into(), b.into(), c.into(), d.into(), e.into()]) + }) }) }) }) }) - .filter(move |phase| (min..max).all(|x| phase.0.contains(&x))) + .filter(move |phase| (min..max).all(|x| phase.0.contains(&x.into()))) } } @@ -92,7 +94,7 @@ struct AmplifierArray { } impl AmplifierArray { - fn new(program: &IntcodeProgram, phase: PhaseSetting) -> AmplifierArray { + fn new(program: &IntcodeProgram, phase: &PhaseSetting) -> AmplifierArray { AmplifierArray { amplifiers: (0..5) .map(|n| AmplifierArray::new_amp(program, phase, n)) @@ -100,11 +102,11 @@ impl AmplifierArray { } } - fn new_amp(program: &IntcodeProgram, phase: PhaseSetting, n: usize) -> IntcodeProgram { + fn new_amp(program: &IntcodeProgram, phase: &PhaseSetting, n: usize) -> IntcodeProgram { if n == 0 { - program.with_input(list![phase.0[n], 0]) + program.with_input(list![phase.0[n].clone(), Intcode::from(0)]) } else { - program.with_input(list![phase.0[n]]) + program.with_input(list![phase.0[n].clone()]) } } @@ -122,7 +124,7 @@ impl AmplifierArray { self.amplifiers .first() .and_then(|amp| amp.input.first().cloned()) - .ok_or(IntcodeProgramError) + .ok_or(IntcodeProgramError::Unknown) } fn is_terminated(&self) -> bool { @@ -1,18 +1,21 @@ +use num::bigint::BigInt; +use num::traits::identities::Zero; +use rpds::RedBlackTreeMap; use rpds::{list::List, vector::Vector}; use std::fmt; use std::iter; use std::iter::FromIterator; use std::iter::IntoIterator; -pub type Intcode = i32; +pub type Intcode = BigInt; #[derive(Debug, Clone)] pub struct IntcodeProgram { - instruction_pointer: usize, - pub error: bool, + instruction_pointer: Intcode, + pub error: Option<IntcodeProgramError>, pub halted: bool, pub awaiting_input: bool, - memory: Vector<Intcode>, + memory: RedBlackTreeMap<Intcode, Intcode>, pub input: List<Intcode>, pub output: Vector<Intcode>, } @@ -20,11 +23,15 @@ pub struct IntcodeProgram { impl FromIterator<Intcode> for IntcodeProgram { fn from_iter<I: IntoIterator<Item = Intcode>>(iter: I) -> Self { IntcodeProgram { - instruction_pointer: 0, - error: false, + instruction_pointer: Intcode::from(0), + error: None, halted: false, awaiting_input: false, - memory: iter.into_iter().collect(), + memory: iter + .into_iter() + .enumerate() + .map(|(addr, val)| (Intcode::from(addr), val)) + .collect(), input: List::new(), output: Vector::new(), } @@ -33,7 +40,8 @@ impl FromIterator<Intcode> for IntcodeProgram { impl IntcodeProgram { pub fn with_noun_verb_input(&self, noun: Intcode, verb: Intcode) -> IntcodeProgram { - self.with_memory_set(1, noun).with_memory_set(2, verb) + self.with_memory_set(1.into(), noun) + .with_memory_set(2.into(), verb) } pub fn with_input(&self, input: List<Intcode>) -> IntcodeProgram { @@ -61,7 +69,7 @@ impl IntcodeProgram { self.run_to_termination().output_into_result() } - pub fn execute_returning_memory_0(&self) -> Result<Option<Intcode>, IntcodeProgramError> { + pub fn execute_returning_memory_0(&self) -> Result<Intcode, IntcodeProgramError> { self.run_to_termination().memory_0_into_result() } @@ -81,7 +89,7 @@ impl IntcodeProgram { .unwrap() } - fn with_instruction_pointer(&self, instruction_pointer: usize) -> IntcodeProgram { + fn with_instruction_pointer(&self, instruction_pointer: Intcode) -> IntcodeProgram { IntcodeProgram { instruction_pointer, ..self.clone() @@ -90,19 +98,16 @@ impl IntcodeProgram { fn with_instruction_pointer_offset(&self, offset: usize) -> IntcodeProgram { IntcodeProgram { - instruction_pointer: self.instruction_pointer + offset, + instruction_pointer: self.instruction_pointer.clone() + offset, ..self.clone() } } - fn with_memory_set(&self, address: usize, value: Intcode) -> IntcodeProgram { - self.memory - .set(address, value) - .map(|memory| IntcodeProgram { - memory, - ..self.clone() - }) - .unwrap_or(self.error()) + fn with_memory_set(&self, address: Intcode, value: Intcode) -> IntcodeProgram { + IntcodeProgram { + memory: self.memory.insert(address, value), + ..self.clone() + } } fn with_input_consumed(&self) -> IntcodeProgram { @@ -112,7 +117,7 @@ impl IntcodeProgram { input, ..self.clone() }) - .unwrap_or(self.error()) + .unwrap_or(self.error(IntcodeProgramError::Unknown)) } fn with_output(&self, print: Intcode) -> IntcodeProgram { @@ -123,25 +128,26 @@ impl IntcodeProgram { } fn output_into_result(&self) -> Result<Vector<Intcode>, IntcodeProgramError> { - if self.error { - Err(IntcodeProgramError) - } else { - Ok(self.output.clone()) + match self.error { + Some(ref error) => Err(error.clone()), + None => Ok(self.output.clone()), } } - fn memory_0_into_result(&self) -> Result<Option<Intcode>, IntcodeProgramError> { - if self.error { - Err(IntcodeProgramError) - } else { - Ok(self.memory.get(0).cloned()) + fn memory_0_into_result(&self) -> Result<Intcode, IntcodeProgramError> { + match self.error { + Some(ref error) => Err(error.clone()), + None => Ok(self + .memory + .get(&Intcode::from(0)) + .cloned() + .unwrap_or(Intcode::from(0))), } } fn next(&self) -> IntcodeProgram { - //eprintln!("{:?}", self); self.memory - .get(self.instruction_pointer) - .map(|&opcode| match opcode % 100 { + .get(&self.instruction_pointer) + .map(|opcode| match opcode.to_radix_le(100).1[0] { 1 => self.add(opcode), 2 => self.multiply(opcode), 3 => self.input(opcode), @@ -151,77 +157,79 @@ impl IntcodeProgram { 7 => self.less_than(opcode), 8 => self.equals(opcode), 99 => self.halt(), - _ => self.error(), + unknown => self.error(IntcodeProgramError::InvalidOpcode(unknown.clone())), }) - .unwrap_or(self.error()) + .unwrap_or(self.error(IntcodeProgramError::Unknown)) } - fn add(&self, mode: Intcode) -> IntcodeProgram { + fn add(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode), self.get_immediate(3)) { - (Some(in1), Some(in2), Some(out)) => self + (in1, in2, out) => self .with_instruction_pointer_offset(4) - .with_memory_set(out as usize, in1 + in2), - _ => self.error(), + .with_memory_set(out, in1 + in2), } } - fn multiply(&self, mode: Intcode) -> IntcodeProgram { + fn multiply(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode), self.get_immediate(3)) { - (Some(in1), Some(in2), Some(out)) => self + (in1, in2, out) => self .with_instruction_pointer_offset(4) - .with_memory_set(out as usize, in1 * in2), - _ => self.error(), + .with_memory_set(out, in1 * in2), } } - fn input(&self, _mode: Intcode) -> IntcodeProgram { + fn input(&self, _mode: &Intcode) -> IntcodeProgram { match (self.input.first().cloned(), self.get_immediate(1)) { - (Some(input), Some(out)) => self + (Some(input), out) => self .with_instruction_pointer_offset(2) - .with_memory_set(out as usize, input) + .with_memory_set(out, input) .with_input_consumed(), - (None, Some(_out)) => self.await_input(), - _ => self.error(), + (None, _out) => self.await_input(), } } - fn output(&self, mode: Intcode) -> IntcodeProgram { + fn output(&self, mode: &Intcode) -> IntcodeProgram { match self.get(1, mode) { - Some(print) => self.with_instruction_pointer_offset(2).with_output(print), - _ => self.error(), + print => self.with_instruction_pointer_offset(2).with_output(print), } } - fn jump_if_true(&self, mode: Intcode) -> IntcodeProgram { + fn jump_if_true(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode)) { - (Some(pred), Some(to)) if pred != 0 => self.with_instruction_pointer(to as usize), - (Some(_), Some(_)) => self.with_instruction_pointer_offset(3), - _ => self.error(), + (ref pred, ref to) if !pred.is_zero() => self.with_instruction_pointer(to.clone()), + (_, _) => self.with_instruction_pointer_offset(3), } } - fn jump_if_false(&self, mode: Intcode) -> IntcodeProgram { + fn jump_if_false(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode)) { - (Some(pred), Some(to)) if pred == 0 => self.with_instruction_pointer(to as usize), - (Some(_), Some(_)) => self.with_instruction_pointer_offset(3), - _ => self.error(), + (ref pred, ref to) if pred.is_zero() => self.with_instruction_pointer(to.clone()), + (_, _) => self.with_instruction_pointer_offset(3), } } - fn less_than(&self, mode: Intcode) -> IntcodeProgram { + fn less_than(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode), self.get_immediate(3)) { - (Some(in1), Some(in2), Some(out)) => self - .with_instruction_pointer_offset(4) - .with_memory_set(out as usize, if in1 < in2 { 1 } else { 0 }), - _ => self.error(), + (in1, in2, out) => self.with_instruction_pointer_offset(4).with_memory_set( + out, + if in1 < in2 { + Intcode::from(1) + } else { + Intcode::from(0) + }, + ), } } - fn equals(&self, mode: Intcode) -> IntcodeProgram { + fn equals(&self, mode: &Intcode) -> IntcodeProgram { match (self.get(1, mode), self.get(2, mode), self.get_immediate(3)) { - (Some(in1), Some(in2), Some(out)) => self - .with_instruction_pointer_offset(4) - .with_memory_set(out as usize, if in1 == in2 { 1 } else { 0 }), - _ => self.error(), + (in1, in2, out) => self.with_instruction_pointer_offset(4).with_memory_set( + out, + if in1 == in2 { + Intcode::from(1) + } else { + Intcode::from(0) + }, + ), } } @@ -246,41 +254,81 @@ impl IntcodeProgram { } } - fn error(&self) -> IntcodeProgram { + fn error(&self, error: IntcodeProgramError) -> IntcodeProgram { IntcodeProgram { halted: true, - error: true, + error: Some(error), ..self.clone() } } - fn get(&self, pointer_offset: usize, mode: Intcode) -> Option<Intcode> { - match mode / (10 as Intcode).pow(pointer_offset as u32 + 1) % 10 { + fn get(&self, pointer_offset: usize, mode: &Intcode) -> Intcode { + match mode + .to_radix_le(10) + .1 + .get(pointer_offset + 2) + .cloned() + .unwrap_or(0) + { 0 => self.get_position(pointer_offset), 1 => self.get_immediate(pointer_offset), - _ => None, + _ => Intcode::from(0), // TODO: Relative mode } } - fn get_immediate(&self, pointer_offset: usize) -> Option<Intcode> { + fn get_immediate(&self, pointer_offset: usize) -> Intcode { self.memory - .get(self.instruction_pointer + pointer_offset) + .get(&(self.instruction_pointer.clone() + pointer_offset)) .cloned() + .unwrap_or(Intcode::from(0)) } - fn get_position(&self, pointer_offset: usize) -> Option<Intcode> { - self.get_immediate(pointer_offset) - .and_then(|r| self.memory.get(r as usize)) + fn get_position(&self, pointer_offset: usize) -> Intcode { + self.memory + .get(&self.get_immediate(pointer_offset)) .cloned() + .unwrap_or(Intcode::from(0)) } } -#[derive(Debug, PartialEq)] -pub struct IntcodeProgramError; +#[derive(Debug, PartialEq, Clone)] +pub enum IntcodeProgramError { + InvalidOpcode(u8), + Unknown, +} impl fmt::Display for IntcodeProgramError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Unknown error") + use IntcodeProgramError::*; + + match self { + InvalidOpcode(i) => write!(f, "{} is not a valid opcode", i), + Unknown => write!(f, "Unknown error"), + } } } impl std::error::Error for IntcodeProgramError {} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn day_2_example_1() { + let program: IntcodeProgram = vec![1, 0, 0, 0, 99] + .into_iter() + .map(Intcode::from) + .collect::<IntcodeProgram>() + .run_to_termination(); + + assert_eq!(program.error, None); + assert_eq!( + program.memory, + vec![2, 0, 0, 0, 99] + .into_iter() + .enumerate() + .map(|(i, val)| (Intcode::from(i), Intcode::from(val))) + .collect::<RedBlackTreeMap<Intcode, Intcode>>() + ); + } +} |