advent_of_code/year2016/
assembunny.rs1pub 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 Copy(ValueOrRegister, Register),
32 Increase(Register),
34 Decrease(Register),
36 Jump(ValueOrRegister, ValueOrRegister),
38 Toggle(Register),
40 Nop,
42 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 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 } 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}