use crate::prelude::*; pub fn solution() -> Solution { Solution::new(Day::new(8, 2020), part_1, part_2).with_expected(1814, 1056) } #[derive(Debug, Eq, PartialEq, Hash, Clone)] enum Operation { Acc, Jmp, Nop, } impl From<&str> for Operation { fn from(input: &str) -> Self { match input { "acc" => Self::Acc, "jmp" => Self::Jmp, "nop" => Self::Nop, _ => unreachable!("{input}"), } } } type Argument = i32; #[derive(Debug, Eq, PartialEq, Hash, Clone)] struct Instruction { line: usize, op: Operation, arg: Argument, } impl From<(usize, &str)> for Instruction { fn from((line, input): (usize, &str)) -> Self { let mut split = input.split(' '); let op = split.next().map(Into::into).unwrap(); let arg = split.next().map(|arg| arg.parse().unwrap()).unwrap(); Self { line, op, arg } } } fn execute(instructions: &[Instruction]) -> (i32, bool) { let mut seen_instructions = HashSet::new(); let mut accumulator = 0; let mut encountered_infinite_loop = false; let mut instruction_position = 0; while let Some(instruction) = instructions.get(instruction_position as usize) { if !seen_instructions.insert(instruction) { encountered_infinite_loop = true; break; } let mut increment_position = 1; match instruction.op { Operation::Acc => accumulator += instruction.arg, Operation::Jmp => increment_position = instruction.arg, Operation::Nop => (), }; instruction_position += increment_position; } (accumulator, encountered_infinite_loop) } fn parse_instructions(input: &str) -> Vec { input.lines().enumerate().map(Into::into).collect() } fn part_1(input: &str) -> Result { Ok(execute(&parse_instructions(input)).0.to_string()) } fn part_2(input: &str) -> Result { let instructions = parse_instructions(input); let mut part_2_result = None; for (index, instruction) in instructions.iter().enumerate() { let mut new_instructions = instructions.clone(); if instruction.op == Operation::Jmp || instruction.op == Operation::Nop { let mut new_instruction = instruction.clone(); new_instruction.op = match instruction.op { Operation::Jmp => Operation::Nop, Operation::Nop => Operation::Jmp, Operation::Acc => Operation::Acc, }; new_instructions[index] = new_instruction; } let (result, encountered_infinite_loop) = execute(&new_instructions); if !encountered_infinite_loop { part_2_result = Some(result.to_string()); break; } } Ok(part_2_result.unwrap()) }