Complete day 8

This commit is contained in:
Tristan Daniël Maat 2020-12-09 00:13:51 +00:00
parent 9c9a5389c6
commit ae48e7b4fb
Signed by: tlater
GPG key ID: 49670FD774E43268
6 changed files with 966 additions and 0 deletions

187
day-8/src/emulator.rs Normal file
View 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
View 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(())
}