advent_of_code/year2016/
assembunny.rs

1pub type Word = i32;
2type Register = u8;
3
4#[derive(Copy, Clone)]
5pub enum ValueOrRegister {
6    Value(Word),
7    Register(Register),
8}
9
10impl ValueOrRegister {
11    fn parse(input: &str) -> Result<Self, String> {
12        Ok(if ["a", "b", "c", "d"].contains(&input) {
13            Self::Register(input.as_bytes()[0] - b'a')
14        } else {
15            Self::Value(input.parse::<Word>().map_err(|_| "Invalid value")?)
16        })
17    }
18}
19
20fn parse_register(input: &str) -> Result<Register, String> {
21    if ["a", "b", "c", "d"].contains(&input) {
22        Ok(input.as_bytes()[0] - b'a')
23    } else {
24        Err("Invalid register - not a/b/c/d".to_string())
25    }
26}
27
28#[derive(Copy, Clone)]
29pub enum Instruction {
30    // cpy x y copies x (either an integer or the value of a register) into register y.
31    Copy(ValueOrRegister, Register),
32    // inc x increases the value of register x by one.
33    Increase(Register),
34    // dec x decreases the value of register x by one.
35    Decrease(Register),
36    // jnz x y jumps to an instruction y away (positive means forward; negative means backward), but only if x is not zero.
37    Jump(ValueOrRegister, ValueOrRegister),
38    // tgl x toggles the instruction x away (see day 23).
39    Toggle(Register),
40    // Instruction that does nothing. Used to implement Toggle.
41    Nop,
42    // out x transmits x (either an integer or the value of a register) as the next value for the clock signal.
43    Out(ValueOrRegister),
44}
45
46impl Instruction {
47    fn parse(input: &str) -> Result<Self, String> {
48        let words = input.split(' ').collect::<Vec<_>>();
49        match words[0] {
50            "cpy" => {
51                if words.len() == 3 {
52                    let first_parameter = ValueOrRegister::parse(words[1])?;
53                    let second_parameter = parse_register(words[2])?;
54                    Ok(Self::Copy(first_parameter, second_parameter))
55                } else {
56                    Err(format!(
57                        "Invalid cpy instruction with {} arguments",
58                        words.len() - 1
59                    ))
60                }
61            }
62            "inc" => {
63                if words.len() == 2 {
64                    Ok(Self::Increase(parse_register(words[1])?))
65                } else {
66                    Err(format!(
67                        "Invalid inc instruction with {} arguments",
68                        words.len() - 1
69                    ))
70                }
71            }
72            "dec" => {
73                if words.len() == 2 {
74                    Ok(Self::Decrease(parse_register(words[1])?))
75                } else {
76                    Err(format!(
77                        "Invalid dec instruction with {} arguments",
78                        words.len() - 1
79                    ))
80                }
81            }
82            "jnz" => {
83                if words.len() == 3 {
84                    let first_parameter = ValueOrRegister::parse(words[1])?;
85                    let second_parameter = ValueOrRegister::parse(words[2])?;
86                    Ok(Self::Jump(first_parameter, second_parameter))
87                } else {
88                    Err(format!(
89                        "Invalid jnz instruction with {} arguments",
90                        words.len() - 1
91                    ))
92                }
93            }
94            "tgl" => {
95                let register = parse_register(words[1])?;
96                Ok(Self::Toggle(register))
97            }
98            "out" => {
99                let parameter = ValueOrRegister::parse(words[1])?;
100                Ok(Self::Out(parameter))
101            }
102            _ => Err("Invalid instruction not starting with cpy, inc, dec or jnz".to_string()),
103        }
104    }
105
106    const fn toggle(self) -> Self {
107        match self {
108            Self::Copy(a, b) => Self::Jump(a, ValueOrRegister::Register(b)),
109            Self::Increase(a) => Self::Decrease(a),
110            Self::Decrease(a) | Self::Toggle(a) => Self::Increase(a),
111            Self::Out(a) => match a {
112                ValueOrRegister::Register(register_value) => Self::Increase(register_value),
113                _ => Self::Nop,
114            },
115            Self::Jump(a, b) => match b {
116                ValueOrRegister::Register(register_value) => Self::Copy(a, register_value),
117                _ => Self::Nop,
118            },
119            Self::Nop => Self::Nop,
120        }
121    }
122}
123
124pub struct Computer {
125    // The assembunny code you've extracted operates on four registers (a, b, c, and d) that start at 0 and can hold any integer
126    pub(crate) registers: [Word; 4],
127    pub(crate) instructions: Vec<Instruction>,
128}
129
130impl Computer {
131    pub(crate) fn parse(input: &str) -> Result<Self, String> {
132        let mut instructions = Vec::new();
133        for line in input.lines() {
134            instructions.push(Instruction::parse(line)?);
135        }
136        Ok(Self {
137            registers: [0, 0, 0, 0],
138            instructions,
139        })
140    }
141
142    pub(crate) fn execute(&mut self) -> Word {
143        let mut current_instruction = 0;
144        'outer: while let Some(&instruction) = self.instructions.get(current_instruction) {
145            match instruction {
146                Instruction::Copy(value_or_register, register) => {
147                    let value = self.value_of(value_or_register);
148                    self.registers[register as usize] = value;
149                }
150                Instruction::Increase(register) => {
151                    self.registers[register as usize] += 1;
152                }
153                Instruction::Decrease(register) => {
154                    self.registers[register as usize] -= 1;
155                }
156                Instruction::Jump(first, second) => {
157                    if self.value_of(first) != 0 {
158                        current_instruction =
159                            (current_instruction as Word + self.value_of(second)) as usize;
160                        continue 'outer;
161                    }
162                }
163                Instruction::Toggle(register) => {
164                    let value = self.registers[register as usize];
165                    let ptr = current_instruction as Word + value;
166                    if ptr < 0 || ptr >= self.instructions.len() as Word {
167                        // If an attempt is made to toggle an instruction outside the program, nothing happens.
168                    } else {
169                        self.instructions[ptr as usize] = self.instructions[ptr as usize].toggle();
170                    }
171                }
172                Instruction::Nop => {}
173                Instruction::Out(_a) => {}
174            }
175
176            current_instruction += 1;
177        }
178
179        self.registers[0]
180    }
181
182    const fn value_of(&self, value_or_register: ValueOrRegister) -> Word {
183        match value_or_register {
184            ValueOrRegister::Value(word) => word,
185            ValueOrRegister::Register(register_idx) => self.registers[register_idx as usize],
186        }
187    }
188}