1
Fork 0
advent-of-code/source/year_2020/day_08/mod.rs

123 lines
3.2 KiB
Rust
Raw Permalink Normal View History

2024-01-14 21:04:10 +00:00
//! Day 08 of 2020.
2022-10-03 16:02:40 +00:00
use crate::prelude::*;
2024-01-14 21:04:10 +00:00
/// Get the solution for day 08 of 2020.
2022-10-03 16:02:40 +00:00
pub fn solution() -> Solution {
Solution::new(Day::new(8, 2020), part_1, part_2).with_expected(1814, 1056)
}
2024-01-14 21:04:10 +00:00
/// The possible operations an instruction can perform.
2022-10-03 16:02:40 +00:00
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
enum Operation {
2024-01-14 21:04:10 +00:00
/// Increase or decrease the accumulator.
2022-10-03 16:02:40 +00:00
Acc,
2024-01-14 21:04:10 +00:00
/// Jump to a new instruction.
2022-10-03 16:02:40 +00:00
Jmp,
2024-01-14 21:04:10 +00:00
/// Do nothing, no operation.
2022-10-03 16:02:40 +00:00
Nop,
}
impl From<&str> for Operation {
fn from(input: &str) -> Self {
match input {
"acc" => Self::Acc,
"jmp" => Self::Jmp,
"nop" => Self::Nop,
_ => unreachable!("{input}"),
}
}
}
2024-01-14 21:04:10 +00:00
/// Type alias for the arguments of operations.
2022-10-03 16:02:40 +00:00
type Argument = i32;
2024-01-14 21:04:10 +00:00
/// A single instruction.
2022-10-03 16:02:40 +00:00
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct Instruction {
2024-01-14 21:04:10 +00:00
/// The line the instruction was parsed from, used for debugging.
2022-10-03 16:02:40 +00:00
line: usize,
2024-01-14 21:04:10 +00:00
/// The operation to perform.
2022-10-03 16:02:40 +00:00
op: Operation,
2024-01-14 21:04:10 +00:00
/// The argument for the operation.
2022-10-03 16:02:40 +00:00
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 }
}
}
2024-01-14 21:04:10 +00:00
/// Execute the list of instructions and return the accumulator and whether or
/// not an infinite loop was encountered.
2022-10-03 16:02:40 +00:00
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)
}
2024-01-14 21:04:10 +00:00
/// Parse the list of instructions from the puzzle input.
2022-10-03 16:02:40 +00:00
fn parse_instructions(input: &str) -> Vec<Instruction> {
input.lines().enumerate().map(Into::into).collect()
}
2024-01-14 21:04:10 +00:00
/// The logic to solve part one.
2022-10-03 16:02:40 +00:00
fn part_1(input: &str) -> Result<String> {
Ok(execute(&parse_instructions(input)).0.to_string())
}
2024-01-14 21:04:10 +00:00
/// The logic to solve part two.
2022-10-03 16:02:40 +00:00
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())
}