diff --git a/rust/08/computer.rs b/rust/08/computer.rs new file mode 100644 index 0000000..a8262f3 --- /dev/null +++ b/rust/08/computer.rs @@ -0,0 +1,137 @@ +pub struct Computer { + instructions: Vec, + ip: usize, + acc: i32, + flipped_idx: Option, +} + +enum Instruction { + Acc(i32), + Jmp(i32), + Nop(i32), +} + +impl Computer { + /// Creates a new computer from a string containing intructions + pub fn new(input: String) -> Computer { + let instructions: Vec = input + .split("\n") + .map(|line| { + let inst = &line[..3]; + let sign_value = match &line[4..5] { + "+" => 1, + "-" => -1, + _ => panic!("Expected + or -, got something else"), + }; + let value_str = &line[5..]; + let value = sign_value + * value_str + .parse::() + .expect(format!("Found an invalid number: {}", value_str).as_str()); + + match inst { + "acc" => Instruction::Acc(value), + "jmp" => Instruction::Jmp(value), + "nop" => Instruction::Nop(value), + _ => panic!("Found an invalid instruction: {}", inst), + } + }) + .collect(); + + Computer { + instructions, + ip: 0, + acc: 0, + flipped_idx: None, + } + } + + /// Returns the number of instructions of this computer + pub fn len(&self) -> usize { + self.instructions.len() + } + + /// Executes the instruction at the current ip + pub fn step(&mut self) { + match self.instructions.get(self.ip) { + Some(Instruction::Acc(value)) => { + self.acc += value; + self.ip += 1; + } + Some(Instruction::Jmp(value)) => { + let ip_i32: i32 = self.ip.try_into().expect("Found an invalid ip"); + let new_value = ip_i32 + value; + + self.ip = new_value.try_into().expect("Tried to assign an invalid ip"); + } + Some(Instruction::Nop(_)) => { + self.ip += 1; + } + None => { + panic!("Tried to execute an out of bounds instruction.") + } + } + } + + /// Flips a JMP into NOP, or NOP into JMP instruction at position `idx`. + /// + /// If this function is called more than once, it panics. + pub fn flip(&mut self, idx: usize) { + match self.flipped_idx { + Some(_) => panic!("An instruction on this computer has already been flipped."), + None => { + self.flipped_idx = Some(idx); + self.flip_instruction(idx); + } + } + } + + /// Internal implementation of flip + fn flip_instruction(&mut self, idx: usize) { + match self.instructions.get(idx) { + Some(Instruction::Jmp(v)) => { + self.instructions[idx] = Instruction::Nop(*v); + } + Some(Instruction::Nop(v)) => { + self.instructions[idx] = Instruction::Jmp(*v); + } + Some(_) => {} + None => { + panic!( + "Tried to flip an instruction on an out of bounds index. {}", + idx + ) + } + } + } + + /// Returns whether the instruction at `idx` is JMP or NOP. + pub fn jmp_or_nop_at(&mut self, idx: usize) -> bool { + match self.instructions.get(idx) { + Some(Instruction::Nop(_)) => true, + Some(Instruction::Jmp(_)) => true, + _ => false, + } + } + + /// Returns the current ip + pub fn get_current_ip(&self) -> usize { + self.ip + } + + /// Returns the current acc + pub fn get_acc(&self) -> i32 { + self.acc + } + + /// Resets the state of the computer, including any JMP/NOP flip. + pub fn reset(&mut self) { + self.ip = 0; + self.acc = 0; + + if let Some(idx) = self.flipped_idx { + self.flipped_idx = None; + self.flip_instruction(idx); + } + } +} diff --git a/rust/08/main.rs b/rust/08/main.rs new file mode 100644 index 0000000..5806661 --- /dev/null +++ b/rust/08/main.rs @@ -0,0 +1,53 @@ +use std::{collections::HashMap, fs::read_to_string}; + +mod computer; + +fn main() { + // current time + let start = std::time::Instant::now(); + + let input = read_to_string("input.txt").expect("Input file not found"); + let mut computer = computer::Computer::new(input); + + let computer_len = computer.len(); + + 'outer: for i in 0..(computer_len) { + // If the instruction at position `i` can't be flipped + if !computer.jmp_or_nop_at(i) { + continue; + } + + // Stores the indexes already executed. + let mut map: HashMap = HashMap::new(); + computer.flip(i); + + loop { + let current_ip = computer.get_current_ip(); + + // Success condition + if current_ip == computer_len { + println!("Result: {}", computer.get_acc()); + break 'outer; + } + + // Loop detected + if map.contains_key(¤t_ip) { + break; + } + + // Store current ip to keep track of what instructions have been executed + map.insert(current_ip, true); + + // Run the current instruction + computer.step(); + } + + // Reset the computer state + computer.reset(); + } + + // Print the time it took to run the program + println!("Time: {} micros", start.elapsed().as_micros()); + + println!("No result was found"); +}