Rewrite day 8 in rust for fun & profit

This commit is contained in:
Araozu 2024-04-25 20:04:24 -05:00
parent e869fc2152
commit 0042d2fbcd
2 changed files with 190 additions and 0 deletions

137
rust/08/computer.rs Normal file
View File

@ -0,0 +1,137 @@
pub struct Computer {
instructions: Vec<Instruction>,
ip: usize,
acc: i32,
flipped_idx: Option<usize>,
}
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<Instruction> = 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::<i32>()
.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);
}
}
}

53
rust/08/main.rs Normal file
View File

@ -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<usize, bool> = 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(&current_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");
}