1use utils::prelude::*;
2
3#[derive(Clone, Debug)]
5pub struct Day23 {
6 instructions: Vec<Instruction>,
7}
8
9#[derive(Copy, Clone, PartialEq, Eq, Debug)]
10enum Register {
11 A,
12 B,
13}
14
15#[derive(Copy, Clone, PartialEq, Eq, Debug)]
16enum Instruction {
17 Half(Register),
18 Triple(Register),
19 Increment(Register),
20 Jump(i16),
21 JumpIfEven(Register, i16),
22 JumpIfOne(Register, i16),
23}
24
25impl Day23 {
26 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
27 let register = parser::literal_map!(
28 "a" => Register::A,
29 "b" => Register::B,
30 );
31
32 Ok(Self {
33 instructions: parser::parse_tree!(
34 ("hlf ", r @ register) => Instruction::Half(r),
35 ("tpl ", r @ register) => Instruction::Triple(r),
36 ("inc ", r @ register) => Instruction::Increment(r),
37 ("jmp ", v @ parser::i16()) => Instruction::Jump(v),
38 ("jie ", r @ register, ", ", o @ parser::i16()) => Instruction::JumpIfEven(r, o),
39 ("jio ", r @ register, ", ", o @ parser::i16()) => Instruction::JumpIfOne(r, o),
40 )
41 .parse_lines(input)?,
42 })
43 }
44
45 #[must_use]
46 pub fn part1(&self) -> u64 {
47 self.execute(0, 0)
48 }
49
50 #[must_use]
51 pub fn part2(&self) -> u64 {
52 self.execute(1, 0)
53 }
54
55 fn execute(&self, mut a: u64, mut b: u64) -> u64 {
56 let mut pc = 0;
57
58 while let Some(&instruction) = pc
59 .try_into()
60 .ok()
61 .and_then(|i: usize| self.instructions.get(i))
62 {
63 pc += 1;
64 match instruction {
65 Instruction::Half(Register::A) => a /= 2,
66 Instruction::Half(Register::B) => b /= 2,
67 Instruction::Triple(Register::A) => a *= 3,
68 Instruction::Triple(Register::B) => b *= 3,
69 Instruction::Increment(Register::A) => a += 1,
70 Instruction::Increment(Register::B) => b += 1,
71 Instruction::Jump(offset) => pc += offset - 1,
72 Instruction::JumpIfEven(Register::A, offset) if a % 2 == 0 => pc += offset - 1,
73 Instruction::JumpIfEven(Register::B, offset) if b % 2 == 0 => pc += offset - 1,
74 Instruction::JumpIfOne(Register::A, offset) if a == 1 => pc += offset - 1,
75 Instruction::JumpIfOne(Register::B, offset) if b == 1 => pc += offset - 1,
76 Instruction::JumpIfEven(_, _) | Instruction::JumpIfOne(_, _) => {}
77 }
78 }
79
80 b
81 }
82}
83
84examples!(Day23 -> (u64, u64) []);