AoC2020/src/day8.rs

138 lines
3.2 KiB
Rust

use std::io::BufRead;
use std::str::FromStr;
#[derive(Debug, PartialEq, Clone)]
enum Op {
Acc,
Nop,
Jmp,
}
impl FromStr for Op {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"acc" => Op::Acc,
"nop" => Op::Nop,
"jmp" => Op::Jmp,
_ => unreachable!()
})
}
}
#[derive(Debug, PartialEq, Clone)]
struct Instruction {
op: Op,
param: isize,
executed: bool,
}
impl FromStr for Instruction {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut it = s.split(' ');
Ok(Instruction {
op: it.next().map(|s| s.parse().ok()).flatten().unwrap(),
param: it.next().unwrap().parse().unwrap(),
executed: false,
})
}
}
fn parse_boot_code<F: BufRead> (input: F) -> Vec<Instruction> {
input.lines()
.filter_map(|line| line.unwrap().parse().ok())
.collect()
}
fn run_boot_code(mut boot_code: Vec<Instruction>) -> Result<isize, isize> {
let mut intrustction_pointer: isize = 0;
let mut acc = 0;
while (intrustction_pointer as usize) < boot_code.len() {
let current_instruction = &mut boot_code[intrustction_pointer as usize];
if current_instruction.executed {
return Err(acc)
} else {
current_instruction.executed = true;
match current_instruction.op {
Op::Acc => {
acc += current_instruction.param;
intrustction_pointer += 1;
},
Op::Nop => intrustction_pointer += 1,
Op::Jmp => intrustction_pointer += current_instruction.param,
}
}
}
Ok(acc)
}
pub fn part1<F: BufRead> (input: F) {
println!("{}", run_boot_code(parse_boot_code(input)).unwrap_err());
}
pub fn part2<F: BufRead> (input: F) {
let boot_code = parse_boot_code(input);
let output = boot_code.iter().enumerate().filter_map(|(idx, instruction)| {
let mut test_code = boot_code.clone();
test_code[idx].op = match instruction.op {
Op::Nop => Op::Jmp,
Op::Jmp => Op::Nop,
_ => return None,
};
run_boot_code(test_code).ok()
}).next().unwrap();
println!("{}", output);
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn test_parse() {
let inputs = [
"nop +0",
"acc +1",
"jmp -4"
];
let expected = [
Instruction {
op: Op::Nop,
param: 0,
executed: false
},
Instruction {
op: Op::Acc,
param: 1,
executed: false
},
Instruction {
op: Op::Jmp,
param: -4,
executed: false
},
];
for (input, item) in inputs.iter().zip(expected.iter()) {
assert_eq!(input.parse::<Instruction>().unwrap(), *item);
}
}
#[test]
pub fn test_boot_code_loop() {
let input = r#"nop +0
acc +1
jmp +4
acc +3
jmp -3
acc -99
acc +1
jmp -4
acc +6"#.as_bytes();
assert_eq!(Err(5), run_boot_code(parse_boot_code(input)));
}
}