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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::Input;

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

#[derive(Debug)]
struct Computer {
    registers: [u32; 2],
    instructions: Vec<Instruction>,
}

impl Computer {
    fn parse_register(specifier: &str) -> Result<u8, String> {
        Ok(match specifier {
            "a" | "a," => 0,
            "b" => 1,
            _ => {
                return Err("Invalid register (not 'a' or 'b')".to_string());
            }
        })
    }

    fn parse(input: &str) -> Result<Self, String> {
        let mut instructions = Vec::new();

        for line in input.lines() {
            let parts = line.split(' ').collect::<Vec<_>>();
            instructions.push(match parts[0] {
                "hlf" => Instruction::Half(Self::parse_register(parts[1])?),
                "tpl" => Instruction::Triple(Self::parse_register(parts[1])?),
                "inc" => Instruction::Increment(Self::parse_register(parts[1])?),
                "jmp" => Instruction::Jump(
                    parts[1]
                        .parse::<i16>()
                        .map_err(|_| "Invalid jmp parameter")?,
                ),
                "jie" => Instruction::JumpIfEven(
                    Self::parse_register(parts[1])?,
                    parts[2]
                        .parse::<i16>()
                        .map_err(|_| "Invalid jie parameter")?,
                ),
                "jio" => Instruction::JumpIfOne(
                    Self::parse_register(parts[1])?,
                    parts[2]
                        .parse::<i16>()
                        .map_err(|_| "Invalid jio parameter")?,
                ),
                _ => {
                    return Err("Invalid instruction".to_string());
                }
            });
        }

        Ok(Self {
            registers: [0, 0],
            instructions,
        })
    }

    fn run(&mut self) {
        let mut instruction_pointer = 0_i16;
        loop {
            if instruction_pointer < 0 {
                break;
            }
            match self.instructions.get(instruction_pointer as usize) {
                Some(&Instruction::Increment(register)) => {
                    self.registers[register as usize] += 1;
                    instruction_pointer += 1;
                }
                Some(&Instruction::Half(register)) => {
                    self.registers[register as usize] /= 2;
                    instruction_pointer += 1;
                }
                Some(&Instruction::Triple(register)) => {
                    self.registers[register as usize] *= 3;
                    instruction_pointer += 1;
                }
                Some(&Instruction::Jump(offset)) => {
                    instruction_pointer += offset;
                }
                Some(&Instruction::JumpIfEven(register, offset)) => {
                    instruction_pointer += if self.registers[register as usize] % 2 == 0 {
                        offset
                    } else {
                        1
                    }
                }
                Some(&Instruction::JumpIfOne(register, offset)) => {
                    instruction_pointer += if self.registers[register as usize] == 1 {
                        offset
                    } else {
                        1
                    }
                }
                None => {
                    break;
                }
            }
        }
    }
}

pub fn solve(input: &mut Input) -> Result<u32, String> {
    let mut computer = Computer::parse(input.text)?;
    computer.registers[0] = input.part_values(0, 1);
    computer.run();
    Ok(computer.registers[1])
}

#[test]
pub fn tests() {
    use crate::{test_part_one, test_part_two};

    let real_input = include_str!("day23_input.txt");
    test_part_one!(real_input => 307);
    test_part_two!(real_input => 160);
}