advent-20/rust/08/computer.rs

138 lines
4.0 KiB
Rust
Raw Permalink Normal View History

2024-04-26 01:04:24 +00:00
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);
}
}
}