Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
OK, I'm really, really happy with how my refactoring on Day 2 came out. Even the addition of the input modes fit into place in nice, neat, decoupled chunks. And then it was a matter of letting my interpreter know about all of the new opcodes.
Note: I was very much not expecting my Day 2 setup to hold up this well. I think I got exceptionally lucky at how today's puzzle was structured. I'm fully expecting to run into a future IntCode puzzle in the coming days that blows my architecture out of the water and makes me start over.
My actual Day 5 module is not very impressive:
/// Day 5: Sunny with a Chance of Asteroids/// /// Test out the Thermal Environment Supervision Terminal/// in the Intcode Interpreterusestd::fs;usecrate::intcode;/// Parses the input. Expects a single line of integers separated by/// commasfnparse_input()->Vec<isize>{lettext:String=fs::read_to_string("data/day5.txt").unwrap();letcleaned=text.trim();cleaned.split(",").map(|c|c.parse().unwrap()).collect()}pubfnrun(){letints=parse_input();letmutinterpreter=intcode::IntcodeInterpreter::new(ints);interpreter.run();}
Here's where all the work gets done. Also, side note, as a happy fallout of how Rust's stdin handles itself, I can either enter values or pipe them in one-per-line, if future input gets more complicated :)
usestd::io;usestd::fs;/// An Intcode Interpreter is a virtual machine that uses opcodes/// to modify its internal memorypubstructIntcodeInterpreter{memory:Vec<isize>,ip:usize,}implIntcodeInterpreter{pubfnnew(memory:Vec<isize>)->Self{Self{memory,ip:0}}/// Sets a memory address to a valuepubfnset(&mutself,position:usize,value:isize){self.memory[position]=value;}/// Reads from a memory addresspubfnget(&self,position:usize)->isize{self.memory[position]}/// Shows the memory for debuggingpubfnprint(&self){println!("{:?}",self.memory);}/// Get the current instructionpubfncurrent_instruction(&self)->isize{self.get(self.ip)%100}/// Runs the program in memory until the stopcode (99) is reached/// /// All new ops should have their own method./// They take no rust args, but read in args as needed and/// shift the instruction pointer when they're done./// Steps should be the number of args used + 1 for the opcodepubfnrun(&mutself){loop{matchself.current_instruction(){1=>self.op1(),2=>self.op2(),3=>self.op3(),4=>self.op4(),5=>self.op5(),6=>self.op6(),7=>self.op7(),8=>self.op8(),99=>return,_=>panic!("Unrecognized opcode {}.",self.get(self.ip)),};}}/// Reads a number from STDINfnread_stdin()->isize{letmutbuffer=String::new();io::stdin().read_line(&mutbuffer).expect("STDIN read failed.");buffer.trim().parse::<isize>().unwrap()}/// Write a number to STDOUTfnwrite_stdout(number:isize){println!("{}",number);}/// Process the parameter mode and provide the value given/// as a parameterfnarg(&self,offset:usize)->isize{letnew_index=(self.ip+offset)%self.memory.len();letmode=(self.memory[self.ip]/10isize.pow(1+offsetasu32))%2;ifmode==1{self.memory[new_index]}elseifmode==0{self.get(self.memory[new_index]asusize)}else{panic!("Unknown parameter mode {}",mode);}}/// Returns the address to write output tofnoutput_address(&self,offset:usize)->usize{letnew_index=(self.ip+offset)%self.memory.len();self.memory[new_index]asusize}/// Steps the IP forward "count" steps, wrapping if neededfnstep(&mutself,count:usize){self.ip=(self.ip+count)%self.memory.len();}/// Add [1] + [2], store in [3]fnop1(&mutself){letin1=self.arg(1);letin2=self.arg(2);letout=self.output_address(3);self.set(out,in1+in2);self.step(4);}/// Mult [1] * [2], store in [3]fnop2(&mutself){letin1=self.arg(1);letin2=self.arg(2);letout=self.output_address(3);self.set(out,in1*in2);self.step(4);}/// Read one value from STDIN and store it in [1]fnop3(&mutself){letout=self.output_address(1);self.set(out,Self::read_stdin());self.step(2);}/// Read [1] and send it to STDOUTfnop4(&mutself){Self::write_stdout(self.arg(1));self.step(2);}/// If [1] != 0, set IP -> [2], else nothingfnop5(&mutself){ifself.arg(1)!=0{self.ip=self.arg(2)asusize;}else{self.step(3);}}/// if [1] == 0, set IP -> [2], else nothingfnop6(&mutself){ifself.arg(1)==0{self.ip=self.arg(2)asusize;}else{self.step(3);}}/// if [1] < [2], set [3] to 1, else 0fnop7(&mutself){letout=self.output_address(3);ifself.arg(1)<self.arg(2){self.set(out,1);}else{self.set(out,0);}self.step(4);}/// if [1] == [2], set [3] to 1, else 0fnop8(&mutself){letout=self.output_address(3);ifself.arg(1)==self.arg(2){self.set(out,1);}else{self.set(out,0);}self.step(4);}}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
OK, I'm really, really happy with how my refactoring on Day 2 came out. Even the addition of the input modes fit into place in nice, neat, decoupled chunks. And then it was a matter of letting my interpreter know about all of the new opcodes.
Note: I was very much not expecting my Day 2 setup to hold up this well. I think I got exceptionally lucky at how today's puzzle was structured. I'm fully expecting to run into a future IntCode puzzle in the coming days that blows my architecture out of the water and makes me start over.
My actual Day 5 module is not very impressive:
Here's where all the work gets done. Also, side note, as a happy fallout of how Rust's
stdin
handles itself, I can either enter values or pipe them in one-per-line, if future input gets more complicated :)