1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use utils::prelude::*;

/// Interpreting assembly.
#[derive(Clone, Debug)]
pub struct Day23 {
    instructions: Vec<Instruction>,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Register {
    A,
    B,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Instruction {
    Half(Register),
    Triple(Register),
    Increment(Register),
    Jump(i16),
    JumpIfEven(Register, i16),
    JumpIfOne(Register, i16),
}

impl Day23 {
    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
        let register = parser::literal_map!(
            "a" => Register::A,
            "b" => Register::B,
        );

        Ok(Self {
            instructions: parser::parse_tree!(
                ("hlf ", r @ register) => Instruction::Half(r),
                ("tpl ", r @ register) => Instruction::Triple(r),
                ("inc ", r @ register) => Instruction::Increment(r),
                ("jmp ", v @ parser::i16()) => Instruction::Jump(v),
                ("jie ", r @ register, ", ", o @ parser::i16()) => Instruction::JumpIfEven(r, o),
                ("jio ", r @ register, ", ", o @ parser::i16()) => Instruction::JumpIfOne(r, o),
            )
            .parse_lines(input)?,
        })
    }

    #[must_use]
    pub fn part1(&self) -> u64 {
        self.execute(0, 0)
    }

    #[must_use]
    pub fn part2(&self) -> u64 {
        self.execute(1, 0)
    }

    fn execute(&self, mut a: u64, mut b: u64) -> u64 {
        let mut pc = 0;

        while let Some(&instruction) = pc
            .try_into()
            .ok()
            .and_then(|i: usize| self.instructions.get(i))
        {
            pc += 1;
            match instruction {
                Instruction::Half(Register::A) => a /= 2,
                Instruction::Half(Register::B) => b /= 2,
                Instruction::Triple(Register::A) => a *= 3,
                Instruction::Triple(Register::B) => b *= 3,
                Instruction::Increment(Register::A) => a += 1,
                Instruction::Increment(Register::B) => b += 1,
                Instruction::Jump(offset) => pc += offset - 1,
                Instruction::JumpIfEven(Register::A, offset) if a % 2 == 0 => pc += offset - 1,
                Instruction::JumpIfEven(Register::B, offset) if b % 2 == 0 => pc += offset - 1,
                Instruction::JumpIfOne(Register::A, offset) if a == 1 => pc += offset - 1,
                Instruction::JumpIfOne(Register::B, offset) if b == 1 => pc += offset - 1,
                Instruction::JumpIfEven(_, _) | Instruction::JumpIfOne(_, _) => {}
            }
        }

        b
    }
}

examples!(Day23 -> (u64, u64) []);