Rewrite day 8 in rust for fun & profit
This commit is contained in:
parent
e869fc2152
commit
0042d2fbcd
137
rust/08/computer.rs
Normal file
137
rust/08/computer.rs
Normal 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
53
rust/08/main.rs
Normal 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(¤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");
|
||||
}
|
Loading…
Reference in New Issue
Block a user