106 lines
2.6 KiB
Rust
106 lines
2.6 KiB
Rust
|
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<Instruction> {
|
||
|
input.lines().enumerate().map(Into::into).collect()
|
||
|
}
|
||
|
|
||
|
fn part_1(input: &str) -> Result<String> {
|
||
|
Ok(execute(&parse_instructions(input)).0.to_string())
|
||
|
}
|
||
|
|
||
|
fn part_2(input: &str) -> Result<String> {
|
||
|
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())
|
||
|
}
|