Complete day 8
This commit is contained in:
parent
9c9a5389c6
commit
ae48e7b4fb
6 changed files with 966 additions and 0 deletions
day-8/src
187
day-8/src/emulator.rs
Normal file
187
day-8/src/emulator.rs
Normal file
|
@ -0,0 +1,187 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use snafu::Snafu;
|
||||
|
||||
pub type Program = Vec<(Instruction, i64)>;
|
||||
|
||||
#[derive(Debug, PartialEq, Snafu)]
|
||||
pub enum EmulatorError {
|
||||
#[snafu(display("Segmentation fault"))]
|
||||
SegmentationFault,
|
||||
#[snafu(display("Infinite loop: {}", accumulator))]
|
||||
InfiniteLoop { accumulator: i64 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Snafu)]
|
||||
pub enum ProgramParseError {
|
||||
#[snafu(display("Invalid instruction given: {}", instruction))]
|
||||
InvalidInstruction { instruction: String },
|
||||
#[snafu(display("Invalid argument given: {}", argument))]
|
||||
InvalidArgument { argument: String },
|
||||
#[snafu(display("Unparseable line: {}", line))]
|
||||
InvalidLine { line: String },
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Instruction {
|
||||
Accumulate,
|
||||
Jump,
|
||||
NoOperation,
|
||||
}
|
||||
|
||||
pub struct Emulator {
|
||||
accumulator: i64,
|
||||
program_counter: usize,
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
accumulator: 0,
|
||||
program_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.accumulator = 0;
|
||||
self.program_counter = 0;
|
||||
}
|
||||
|
||||
fn execute_instruction(
|
||||
&mut self,
|
||||
instruction: Instruction,
|
||||
argument: i64,
|
||||
) -> Result<(), EmulatorError> {
|
||||
match instruction {
|
||||
Instruction::Accumulate => self.accumulator += argument,
|
||||
Instruction::Jump => {
|
||||
self.program_counter = self.program_counter.wrapping_add(argument as usize - 1)
|
||||
}
|
||||
Instruction::NoOperation => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_program_finitely(&mut self, program: &Program) -> Result<i64, EmulatorError> {
|
||||
let mut executed = HashSet::new();
|
||||
|
||||
loop {
|
||||
let (instruction, argument) = program
|
||||
.get(self.program_counter)
|
||||
.ok_or(EmulatorError::SegmentationFault)?;
|
||||
executed.insert(self.program_counter);
|
||||
|
||||
self.execute_instruction(*instruction, *argument)?;
|
||||
|
||||
self.program_counter += 1;
|
||||
|
||||
if executed.contains(&self.program_counter) {
|
||||
break Err(EmulatorError::InfiniteLoop {
|
||||
accumulator: self.accumulator,
|
||||
});
|
||||
} else if self.program_counter >= program.len() {
|
||||
break Ok(self.accumulator);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_code(input: &str) -> Result<Program, ProgramParseError> {
|
||||
input
|
||||
.lines()
|
||||
.map(|instruction| {
|
||||
let (name, argument): (&str, &str) = {
|
||||
let temp: Vec<&str> = instruction.split(' ').collect();
|
||||
if temp.len() != 2 {
|
||||
Err(ProgramParseError::InvalidLine {
|
||||
line: instruction.to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok((temp[0], temp[1]))
|
||||
}
|
||||
}?;
|
||||
|
||||
Ok((
|
||||
match name {
|
||||
"acc" => Instruction::Accumulate,
|
||||
"jmp" => Instruction::Jump,
|
||||
"nop" => Instruction::NoOperation,
|
||||
_ => Err(ProgramParseError::InvalidInstruction {
|
||||
instruction: name.to_string(),
|
||||
})?,
|
||||
},
|
||||
argument
|
||||
.parse()
|
||||
.or(Err(ProgramParseError::InvalidArgument {
|
||||
argument: argument.to_string(),
|
||||
}))?,
|
||||
))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use indoc::indoc;
|
||||
|
||||
#[test]
|
||||
fn test_parsing() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let input = indoc!(
|
||||
"nop +0
|
||||
acc +1
|
||||
jmp +4
|
||||
acc +3
|
||||
jmp -3
|
||||
acc -99
|
||||
acc +1
|
||||
jmp -4
|
||||
acc +6
|
||||
"
|
||||
);
|
||||
|
||||
let code = parse_code(&input)?;
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
(Instruction::NoOperation, 0),
|
||||
(Instruction::Accumulate, 1),
|
||||
(Instruction::Jump, 4),
|
||||
(Instruction::Accumulate, 3),
|
||||
(Instruction::Jump, -3),
|
||||
(Instruction::Accumulate, -99),
|
||||
(Instruction::Accumulate, 1),
|
||||
(Instruction::Jump, -4),
|
||||
(Instruction::Accumulate, 6),
|
||||
]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_running() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let input = indoc!(
|
||||
"nop +0
|
||||
acc +1
|
||||
jmp +4
|
||||
acc +3
|
||||
jmp -3
|
||||
acc -99
|
||||
acc +1
|
||||
jmp -4
|
||||
acc +6
|
||||
"
|
||||
);
|
||||
|
||||
let code = parse_code(&input)?;
|
||||
let mut emulator = Emulator::new();
|
||||
assert_eq!(
|
||||
emulator.run_program_finitely(&code),
|
||||
Err(EmulatorError::InfiniteLoop { accumulator: 5 })
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
49
day-8/src/main.rs
Normal file
49
day-8/src/main.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::fs;
|
||||
|
||||
mod emulator;
|
||||
|
||||
use emulator::{parse_code, Emulator, EmulatorError, Instruction};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let input = fs::read_to_string("input")?;
|
||||
let program = parse_code(&input)?;
|
||||
|
||||
// Part 1
|
||||
let mut emulator = Emulator::new();
|
||||
if let Err(EmulatorError::InfiniteLoop { accumulator }) =
|
||||
emulator.run_program_finitely(&program)
|
||||
{
|
||||
println!("{}", accumulator);
|
||||
} else {
|
||||
panic!("We should get an infinite loop.");
|
||||
}
|
||||
|
||||
// Part 2
|
||||
let result = program
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, instruction)| match instruction {
|
||||
(Instruction::Jump, arg) => {
|
||||
let mut program = program.clone();
|
||||
program[i] = (Instruction::NoOperation, *arg);
|
||||
emulator.reset();
|
||||
emulator.run_program_finitely(&program).ok()
|
||||
}
|
||||
(Instruction::NoOperation, arg) => {
|
||||
let mut program = program.clone();
|
||||
program[i] = (Instruction::Jump, *arg);
|
||||
emulator.reset();
|
||||
emulator.run_program_finitely(&program).ok()
|
||||
}
|
||||
(_, _) => None,
|
||||
})
|
||||
.next();
|
||||
|
||||
if let Some(result) = result {
|
||||
println!("{}", result);
|
||||
} else {
|
||||
panic!("We should have at least one non-infinte loop.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue