~janbaudisch/aoc-2020

097bcee0c7f0bb3cf0f4115622c12067b8506b01 — Jan Baudisch 6 months ago 6e5cbae
add day 8
6 files changed, 166 insertions(+), 1 deletions(-)

M Cargo.lock
M Cargo.toml
A day_08/Cargo.toml
A day_08/src/code.rs
A day_08/src/instruction.rs
A day_08/src/main.rs
M Cargo.lock => Cargo.lock +7 -0
@@ 52,3 52,10 @@ version = "1.0.0"
dependencies = [
 "common",
]

[[package]]
name = "day_08"
version = "1.0.0"
dependencies = [
 "common",
]

M Cargo.toml => Cargo.toml +2 -1
@@ 7,7 7,8 @@ members = [
    "day_04",
    "day_05",
    "day_06",
    "day_07"
    "day_07",
    "day_08"
]

[profile.release]

A day_08/Cargo.toml => day_08/Cargo.toml +12 -0
@@ 0,0 1,12 @@
[package]
name = "day_08"
version = "1.0.0"
description = "Day 8 - Advent of Code 2020"
authors = ["Jan Baudisch <jan@baudisch.xyz>"]
license = "GPL-3.0-or-later"
readme = "../README.md"
edition = "2018"
workspace = ".."

[dependencies]
common = { path = "../common" }

A day_08/src/code.rs => day_08/src/code.rs +67 -0
@@ 0,0 1,67 @@
use crate::instruction::Instruction;

#[derive(Clone, Debug)]
pub struct Code {
    instructions: Vec<Instruction>,
    pointer: usize,
    visited: Vec<usize>,
    pub accumulator: i32,
}

impl From<Vec<String>> for Code {
    fn from(input: Vec<String>) -> Self {
        let instructions = input.iter().map(|x| x.into()).collect();

        Self {
            instructions,
            pointer: 0,
            visited: Vec::new(),
            accumulator: 0,
        }
    }
}

impl Code {
    fn step(&mut self) {
        self.visited.push(self.pointer);

        let mut pointer = self.pointer as isize;

        match self.instructions[self.pointer] {
            Instruction::Accumulate(i) => self.accumulator += i,
            Instruction::Jump(i) => pointer += i as isize - 1,
            Instruction::Noop(_) => {}
        }

        self.pointer = pointer as usize + 1;
    }

    pub fn run(&mut self) {
        while !self.visited.contains(&self.pointer) && self.pointer < self.instructions.len() {
            self.step();
        }
    }

    pub fn repair(&mut self) {
        let mut test_pointer = self.instructions.len();

        loop {
            test_pointer -= 1;

            let mut test = self.clone();

            match test.instructions[test_pointer] {
                Instruction::Jump(i) => test.instructions[test_pointer] = Instruction::Noop(i),
                Instruction::Noop(i) => test.instructions[test_pointer] = Instruction::Jump(i),
                _ => {}
            }

            test.run();

            if test.pointer == test.instructions.len() {
                self.instructions = test.instructions;
                return;
            }
        }
    }
}

A day_08/src/instruction.rs => day_08/src/instruction.rs +19 -0
@@ 0,0 1,19 @@
#[derive(Clone, Copy, Debug)]
pub enum Instruction {
    Accumulate(i32),
    Jump(i32),
    Noop(i32),
}

impl From<&String> for Instruction {
    fn from(input: &String) -> Self {
        let mut parts = input.split_whitespace();

        match parts.next().unwrap() {
            "acc" => Self::Accumulate(i32::from_str_radix(parts.next().unwrap(), 10).unwrap()),
            "jmp" => Self::Jump(i32::from_str_radix(parts.next().unwrap(), 10).unwrap()),
            "nop" => Self::Noop(i32::from_str_radix(parts.next().unwrap(), 10).unwrap()),
            _ => panic!("error converting input"),
        }
    }
}

A day_08/src/main.rs => day_08/src/main.rs +59 -0
@@ 0,0 1,59 @@
mod code;
mod instruction;

use code::Code;
use common::input;

fn main() {
    let code: Code = input::read_lines().into();

    let mut part_one = code.clone();
    part_one.run();

    println!(
        "[PART ONE] accumulator before loop: {}",
        part_one.accumulator
    );

    let mut part_two = code;
    part_two.repair();
    part_two.run();

    println!(
        "[PART TWO] accumulator after termintation: {}",
        part_two.accumulator
    );
}

#[cfg(test)]
mod tests {
    use super::Code;

    fn generate_code() -> Code {
        let input = vec![
            "nop +0", "acc +1", "jmp +4", "acc +3", "jmp -3", "acc -99", "acc +1", "jmp -4",
            "acc +6",
        ];

        input
            .iter()
            .map(|x| x.to_string())
            .collect::<Vec<String>>()
            .into()
    }

    #[test]
    fn part_one() {
        let mut code = generate_code();
        code.run();
        assert_eq!(code.accumulator, 5);
    }

    #[test]
    fn part_two() {
        let mut code = generate_code();
        code.repair();
        code.run();
        assert_eq!(code.accumulator, 8);
    }
}