advent_of_code/year2018/
elfcode.rs

1#[derive(Copy, Clone, PartialEq, Eq)]
2pub struct Registers {
3    pub values: [u64; 6],
4}
5
6#[derive(Copy, Clone)]
7pub struct Instruction {
8    opcode: Opcode,
9    pub a: u64,
10    pub b: u64,
11    pub c: u64,
12}
13
14pub struct Program {
15    pub instruction_pointer_index: u8,
16    pub instructions: Vec<Instruction>,
17    pub registers: Registers,
18}
19
20impl Program {
21    pub fn instruction_pointer(&self) -> Result<u64, String> {
22        self.registers
23            .values
24            .get(self.instruction_pointer_index as usize)
25            .copied()
26            .ok_or_else(|| "Invalid instruction pointer".to_string())
27    }
28
29    pub fn execute_one_instruction(&mut self) -> Result<bool, String> {
30        let ip = self.instruction_pointer()?;
31        if ip as usize >= self.instructions.len() {
32            return Ok(false);
33        }
34        let instruction = self.instructions[ip as usize];
35        self.registers.apply(
36            instruction.opcode,
37            instruction.a,
38            instruction.b,
39            instruction.c,
40        );
41        self.registers.values[self.instruction_pointer_index as usize] += 1;
42        Ok(true)
43    }
44
45    pub fn execute_until_halt(&mut self, max_instructions: u32) -> Result<u64, String> {
46        let mut loop_count = 0;
47        while self.execute_one_instruction()? {
48            loop_count += 1;
49            if loop_count > max_instructions {
50                return Err(format!("Aborting after {max_instructions} instructions"));
51            }
52        }
53        Ok(self.registers.values[0])
54    }
55
56    pub fn parse(input_string: &str) -> Result<Self, String> {
57        let mut lines = input_string.lines();
58        let first_line = lines.next().ok_or("Empty input")?;
59
60        if first_line.len() < 5 {
61            return Err("Invalid first line of elfcode".to_string());
62        }
63        let error = |_| "Invalid elfcode instruction";
64        let instruction_pointer_index = (first_line[4..]).parse::<u8>().map_err(error)?;
65
66        let mut instructions = Vec::new();
67        for line in lines {
68            let parts: Vec<&str> = line.split_whitespace().collect();
69            if parts.len() != 4 {
70                return Err("Invalid elfcode - not four words for instruction".into());
71            }
72            let opcode = opcode_from_str(parts[0])?;
73            let a = parts[1].parse::<u64>().map_err(error)?;
74            let b = parts[2].parse::<u64>().map_err(error)?;
75            let c = parts[3].parse::<u64>().map_err(error)?;
76            instructions.push(Instruction { opcode, a, b, c });
77        }
78
79        Ok(Self {
80            instruction_pointer_index,
81            instructions,
82            registers: Registers::new(),
83        })
84    }
85
86    pub fn optimize(&mut self) {
87        for (line, instruction) in self.instructions.iter_mut().enumerate() {
88            match instruction.opcode {
89                Opcode::Addi => {
90                    if instruction.a as u8 == self.instruction_pointer_index {
91                        instruction.opcode = Opcode::Seti;
92                        instruction.a = line as u64 + instruction.b;
93                        instruction.b = 0; // ignored
94                    }
95                }
96                Opcode::Mulr => {
97                    if instruction.a as u8 == self.instruction_pointer_index
98                        && instruction.b as u8 == self.instruction_pointer_index
99                    {
100                        instruction.opcode = Opcode::Seti;
101                        instruction.a = line as u64 * line as u64;
102                        instruction.b = 0; // ignored
103                    }
104                }
105                Opcode::Muli => {
106                    if instruction.a as u8 == self.instruction_pointer_index {
107                        instruction.opcode = Opcode::Seti;
108                        instruction.a = line as u64 * instruction.b;
109                        instruction.b = 0; // ignored
110                    }
111                }
112                _ => {}
113            }
114        }
115    }
116}
117
118#[derive(Copy, Clone, Eq, PartialEq, Hash)]
119pub enum Opcode {
120    Addr, // (add register) stores into register C the result of adding register A and register B
121    Addi, // (add immediate) stores into register C the result of adding register A and value B.
122    Mulr, // (multiply register) stores into register C the result of multiplying register A and register B.
123    Muli, // (multiply immediate) stores into register C the result of multiplying register A and value B.
124    Banr, // (bitwise AND register) stores into register C the result of the bitwise AND of register A and register B.
125    Bani, // (bitwise AND immediate) stores into register C the result of the bitwise AND of register A and value B.
126    Borr, // (bitwise OR register) stores into register C the result of the bitwise OR of register A and register B.
127    Bori, // (bitwise OR immediate) stores into register C the result of the bitwise OR of register A and value B.
128    Setr, // (set register) copies the contents of register A into register C. (Input B is ignored.)
129    Seti, // (set immediate) stores value A into register C. (Input B is ignored.)
130    Gtir, // (greater-than immediate/register) sets register C to 1 if value A is greater than register B. Otherwise, register C is set to 0.
131    Gtri, // (greater-than register/immediate) sets register C to 1 if register A is greater than value B. Otherwise, register C is set to 0.
132    Gtrr, // (greater-than register/register) sets register C to 1 if register A is greater than register B. Otherwise, register C is set to 0.
133    Eqir, // (equal immediate/register) sets register C to 1 if value A is equal to register B. Otherwise, register C is set to 0.
134    Eqri, // (equal register/immediate) sets register C to 1 if register A is equal to value B. Otherwise, register C is set to 0.
135    Eqrr, // (equal register/register) sets register C to 1 if register A is equal to register B. Otherwise, register C is set to 0.
136}
137
138fn opcode_from_str(name: &str) -> Result<Opcode, String> {
139    Ok(match name {
140        "addr" => Opcode::Addr,
141        "addi" => Opcode::Addi,
142        "mulr" => Opcode::Mulr,
143        "muli" => Opcode::Muli,
144        "banr" => Opcode::Banr,
145        "bani" => Opcode::Bani,
146        "borr" => Opcode::Borr,
147        "bori" => Opcode::Bori,
148        "setr" => Opcode::Setr,
149        "seti" => Opcode::Seti,
150        "gtir" => Opcode::Gtir,
151        "gtri" => Opcode::Gtri,
152        "gtrr" => Opcode::Gtrr,
153        "eqir" => Opcode::Eqir,
154        "eqri" => Opcode::Eqri,
155        "eqrr" => Opcode::Eqrr,
156        _ => {
157            return Err(format!("No matching opcode: {name}"));
158        }
159    })
160}
161
162impl Registers {
163    pub(crate) const fn new() -> Self {
164        Self {
165            values: [0, 0, 0, 0, 0, 0],
166        }
167    }
168
169    const fn reg(&self, index: u64) -> u64 {
170        self.values[index as usize]
171    }
172
173    pub(crate) fn apply(&mut self, opcode: Opcode, a: u64, b: u64, c: u64) {
174        let c = c as usize;
175        self.values[c] = match opcode {
176            Opcode::Addr => self.reg(a) + self.reg(b),
177            Opcode::Addi => self.reg(a) + b,
178            Opcode::Mulr => self.reg(a) * self.reg(b),
179            Opcode::Muli => self.reg(a) * b,
180            Opcode::Banr => self.reg(a) & self.reg(b),
181            Opcode::Bani => self.reg(a) & b,
182            Opcode::Borr => self.reg(a) | self.reg(b),
183            Opcode::Bori => self.reg(a) | b,
184            Opcode::Setr => self.reg(a),
185            Opcode::Seti => a,
186            Opcode::Gtir => u64::from(a > self.reg(b)),
187            Opcode::Gtri => u64::from(self.reg(a) > b),
188            Opcode::Gtrr => u64::from(self.reg(a) > self.reg(b)),
189            Opcode::Eqir => u64::from(a == self.reg(b)),
190            Opcode::Eqri => u64::from(self.reg(a) == b),
191            Opcode::Eqrr => u64::from(self.reg(a) == self.reg(b)),
192        }
193    }
194}