From c554811fda72277df9685e281ba8ea28e8497278 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 19 Dec 2018 07:53:09 +0100 Subject: [PATCH] Clean up day 16 and 19. Day 16 and 19 share the same CPU and now they actually share the same code for it. --- 2018/src/cpu.rs | 167 ++++++++++++++++++++++++++++++++++++++++++++++ 2018/src/day16.rs | 140 ++++++-------------------------------- 2018/src/day19.rs | 140 +++----------------------------------- 2018/src/lib.rs | 1 + 4 files changed, 197 insertions(+), 251 deletions(-) create mode 100644 2018/src/cpu.rs diff --git a/2018/src/cpu.rs b/2018/src/cpu.rs new file mode 100644 index 0000000..8002951 --- /dev/null +++ b/2018/src/cpu.rs @@ -0,0 +1,167 @@ +//! Shared CPU module +//! +//! Day 16 and day 19 use the same CPU, so that code is shared (and documented) here. + +/// Representation an OpCode for the AoC 2018 CPU. +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum OpCode { + ADDR, + ADDI, + MULR, + MULI, + BANR, + BANI, + BORR, + BORI, + SETR, + SETI, + GTIR, + GTRI, + GTRR, + EQIR, + EQRI, + EQRR, +} + +impl OpCode { + /// Check if the given before/after state is valid for a particular OpCode. + /// + /// For this, a possible op is represented as an array of integers, where the first + /// integer (the possible op-code) is ignored. + /// + /// # Examples + /// + /// ``` + /// use aoc_2018::cpu::OpCode; + /// + /// assert_eq!(true, OpCode::ADDI.is_valid(&[0, 0, 3, 0], &[0; 6], &[3, 0, 0, 0, 0, 0])) + /// ``` + pub fn is_valid(self, op: &[i32; 4], before: &[i32; 6], after: &[i32; 6]) -> bool { + let mut cpu = CPU::new(); + + cpu.registers.copy_from_slice(before); + if let Ok(val) = cpu.execute(self, &op[1..4]) { + if val == after[op[3] as usize] { + return true; + } + } + + false + } + + /// Iterator over all possible OpCode values. + /// + /// This iterator is backed internally by a static array of all op codes. + pub fn values() -> impl Iterator { + OP_LIST.iter().cloned() + } +} + +impl From<&str> for OpCode { + fn from(name: &str) -> Self { + match name { + "addr" => OpCode::ADDR, + "addi" => OpCode::ADDI, + "mulr" => OpCode::MULR, + "muli" => OpCode::MULI, + "banr" => OpCode::BANR, + "bani" => OpCode::BANI, + "borr" => OpCode::BORR, + "bori" => OpCode::BORI, + "setr" => OpCode::SETR, + "seti" => OpCode::SETI, + "gtir" => OpCode::GTIR, + "gtri" => OpCode::GTRI, + "gtrr" => OpCode::GTRR, + "eqir" => OpCode::EQIR, + "eqri" => OpCode::EQRI, + "eqrr" => OpCode::EQRR, + _ => panic!("Invalid opcode {}", name), + } + } +} + +const OP_LIST: [OpCode; 16] = [ + OpCode::ADDR, + OpCode::ADDI, + OpCode::MULR, + OpCode::MULI, + OpCode::BANR, + OpCode::BANI, + OpCode::BORR, + OpCode::BORI, + OpCode::SETR, + OpCode::SETI, + OpCode::GTIR, + OpCode::GTRI, + OpCode::GTRR, + OpCode::EQIR, + OpCode::EQRI, + OpCode::EQRR, +]; + +/// CPU Error +/// +/// This can be returned as an error when the CPU cannot execute +/// an instruction. +#[derive(Debug)] +pub enum CPUErr { + /// An invalid register was requested, with this register number. + InvalidRegister(i32), +} + +#[derive(Default)] +pub struct CPU { + /// Internal register state. Can be modified freely. + pub registers: [i32; 6], +} + +impl CPU { + /// Construct a new CPU instance. + pub fn new() -> Self { + Default::default() + } + + /// Execute an OpCode on the CPU + /// + /// This method will return the result of the operation executed, or a + /// CPUErr when execution fails. + /// + /// # Panics + /// + /// This function does not check bounds on the operands slice (var) and the output register. + /// Thus, when the operands slice has less than 3 elements or the target register does not + /// exist, this function will panic. + pub fn execute(&mut self, op: OpCode, var: &[i32]) -> Result { + use self::OpCode::*; + let res = match op { + ADDR => self.reg(var[0])? + self.reg(var[1])?, + ADDI => self.reg(var[0])? + var[1], + MULR => self.reg(var[0])? * self.reg(var[1])?, + MULI => self.reg(var[0])? * var[1], + BANR => self.reg(var[0])? & self.reg(var[1])?, + BANI => self.reg(var[0])? & var[1], + BORR => self.reg(var[0])? | self.reg(var[1])?, + BORI => self.reg(var[0])? | var[1], + SETR => self.reg(var[0])?, + SETI => var[0], + GTRR => (self.reg(var[0])? > self.reg(var[1])?).into(), + GTIR => (var[0] > self.reg(var[1])?).into(), + GTRI => (self.reg(var[0])? > var[1]).into(), + EQRR => (self.reg(var[0])? == self.reg(var[1])?).into(), + EQIR => (var[0] == self.reg(var[1])?).into(), + EQRI => (self.reg(var[0])? == var[1]).into(), + }; + + self.registers[var[2] as usize] = res; + Ok(res) + } + + fn reg(&self, index: i32) -> Result { + if let Some(val) = self.registers.get(index as usize) { + Ok(*val) + } else { + Err(CPUErr::InvalidRegister(index)) + } + } +} diff --git a/2018/src/day16.rs b/2018/src/day16.rs index def40ca..be68407 100644 --- a/2018/src/day16.rs +++ b/2018/src/day16.rs @@ -6,105 +6,8 @@ use std::io::Read; use regex::Regex; use common::Solution; - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -enum OpCode { - ADDR, - ADDI, - MULR, - MULI, - BANR, - BANI, - BORR, - BORI, - SETR, - SETI, - GTIR, - GTRI, - GTRR, - EQIR, - EQRI, - EQRR, -} - -impl OpCode { - fn valid(self, op: &[i32; 4], before: &[i32; 4], after: &[i32; 4]) -> bool { - let mut cpu: CPU = Default::default(); - - cpu.registers.copy_from_slice(before); - if let Ok(val) = cpu.execute(self, &op[1..4]) { - if val == after[op[3] as usize] { - return true; - } - } - - false - } -} - -const OP_LIST: [OpCode; 16] = [ - OpCode::ADDR, - OpCode::ADDI, - OpCode::MULR, - OpCode::MULI, - OpCode::BANR, - OpCode::BANI, - OpCode::BORR, - OpCode::BORI, - OpCode::SETR, - OpCode::SETI, - OpCode::GTIR, - OpCode::GTRI, - OpCode::GTRR, - OpCode::EQIR, - OpCode::EQRI, - OpCode::EQRR, -]; - -#[derive(Debug)] -enum CPUErr { - InvalidRegister(i32), -} - -#[derive(Default)] -struct CPU { - registers: [i32; 4], -} - -impl CPU { - pub fn execute(&mut self, op: OpCode, var: &[i32]) -> Result { - use self::OpCode::*; - let res = match op { - ADDR => self.reg(var[0])? + self.reg(var[1])?, - ADDI => self.reg(var[0])? + var[1], - MULR => self.reg(var[0])? * self.reg(var[1])?, - MULI => self.reg(var[0])? * var[1], - BANR => self.reg(var[0])? & self.reg(var[1])?, - BANI => self.reg(var[0])? & var[1], - BORR => self.reg(var[0])? | self.reg(var[1])?, - BORI => self.reg(var[0])? | var[1], - SETR => self.reg(var[0])?, - SETI => var[0], - GTRR => (self.reg(var[0])? > self.reg(var[1])?).into(), - GTIR => (var[0] > self.reg(var[1])?).into(), - GTRI => (self.reg(var[0])? > var[1]).into(), - EQRR => (self.reg(var[0])? == self.reg(var[1])?).into(), - EQIR => (var[0] == self.reg(var[1])?).into(), - EQRI => (self.reg(var[0])? == var[1]).into(), - }; - - self.registers[var[2] as usize] = res; - Ok(res) - } - - fn reg(&self, index: i32) -> Result { - if let Some(val) = self.registers.get(index as usize) { - Ok(*val) - } else { - Err(CPUErr::InvalidRegister(index)) - } - } -} +use cpu::OpCode; +use cpu::CPU; pub struct Day16 { matcher: Regex, @@ -119,15 +22,15 @@ impl Day16 { } } - fn read(&mut self, reader: &mut BufRead, target: &mut [i32; 4]) -> bool { + fn read(&mut self, reader: &mut BufRead, target: &mut [i32]) -> bool { self.buf.clear(); if reader.read_line(&mut self.buf).is_err() { return false; } if let Some(captures) = self.matcher.captures(&self.buf) { - for i in 0..4 { - target[i] = captures[i + 1].parse().unwrap(); + for (target, cap) in target.iter_mut().zip(captures.iter().skip(1)) { + *target = cap.unwrap().as_str().parse().unwrap(); } true @@ -143,22 +46,21 @@ impl Day16 { HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), ]; - let mut before = [0; 4]; + let mut before = [0; 6]; let mut op = [0; 4]; - let mut after = [0; 4]; - while self.read(&mut reader, &mut before) { + let mut after = [0; 6]; + while self.read(&mut reader, &mut before[..4]) { self.read(&mut reader, &mut op); - self.read(&mut reader, &mut after); + self.read(&mut reader, &mut after[..4]); reader.read_line(&mut self.buf).unwrap_or(0); if mappings[op[0] as usize].is_empty() { - mappings[op[0] as usize].extend(OP_LIST.iter() - .filter(|x| x.valid(&op, &before, &after)) - .cloned()); + mappings[op[0] as usize].extend(OpCode::values() + .filter(|x| x.is_valid(&op, &before, &after))); } else { - for option in OP_LIST.iter() - .filter(|x| !x.valid(&op, &before, &after)) { - mappings[op[0] as usize].remove(option); + for option in OpCode::values() + .filter(|x| !x.is_valid(&op, &before, &after)) { + mappings[op[0] as usize].remove(&option); } continue; } @@ -207,18 +109,18 @@ impl Solution for Day16 { fn part1(&mut self, input: &mut Read) -> String { let mut reader = BufReader::new(input); - let mut before = [0; 4]; + let mut before = [0; 6]; let mut op = [0; 4]; - let mut after = [0; 4]; + let mut after = [0; 6]; let mut counter = 0; - while self.read(&mut reader, &mut before) { + while self.read(&mut reader, &mut before[..4]) { self.read(&mut reader, &mut op); - self.read(&mut reader, &mut after); + self.read(&mut reader, &mut after[..4]); reader.read_line(&mut self.buf).unwrap_or(0); - let valid = OP_LIST.iter() - .filter(|x| x.valid(&op, &before, &after)) + let valid = OpCode::values() + .filter(|x| x.is_valid(&op, &before, &after)) .count(); if valid >= 3 { @@ -238,7 +140,7 @@ impl Solution for Day16 { // Skip a line reader.read_line(&mut self.buf).unwrap(); - let mut cpu: CPU = Default::default(); + let mut cpu = CPU::new(); while self.read(&mut reader, &mut op) { cpu.execute(mapping[op[0] as usize], &op[1..4]).unwrap(); diff --git a/2018/src/day19.rs b/2018/src/day19.rs index 558e878..369dbfd 100644 --- a/2018/src/day19.rs +++ b/2018/src/day19.rs @@ -1,134 +1,10 @@ +use std::io::BufRead; +use std::io::BufReader; use std::io::Read; use common::Solution; -use std::io::BufReader; -use std::io::BufRead; - -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -enum OpCode { - ADDR, - ADDI, - MULR, - MULI, - BANR, - BANI, - BORR, - BORI, - SETR, - SETI, - GTIR, - GTRI, - GTRR, - EQIR, - EQRI, - EQRR, -} - -impl OpCode { - fn valid(self, op: &[i32; 4], before: &[i32; 4], after: &[i32; 4]) -> bool { - let mut cpu = CPU::new(); - - cpu.registers.copy_from_slice(before); - if let Ok(val) = cpu.execute(self, &op[1..4]) { - if val == after[op[3] as usize] { - return true; - } - } - - false - } -} - -impl From<&str> for OpCode { - fn from(name: &str) -> Self { - match name { - "addr" => OpCode::ADDR, - "addi" => OpCode::ADDI, - "mulr" => OpCode::MULR, - "muli" => OpCode::MULI, - "banr" => OpCode::BANR, - "bani" => OpCode::BANI, - "borr" => OpCode::BORR, - "bori" => OpCode::BORI, - "setr" => OpCode::SETR, - "seti" => OpCode::SETI, - "gtir" => OpCode::GTIR, - "gtri" => OpCode::GTRI, - "gtrr" => OpCode::GTRR, - "eqir" => OpCode::EQIR, - "eqri" => OpCode::EQRI, - "eqrr" => OpCode::EQRR, - _ => panic!("Invalid opcode {}", name), - } - } -} - -const OP_LIST: [OpCode; 16] = [ - OpCode::ADDR, - OpCode::ADDI, - OpCode::MULR, - OpCode::MULI, - OpCode::BANR, - OpCode::BANI, - OpCode::BORR, - OpCode::BORI, - OpCode::SETR, - OpCode::SETI, - OpCode::GTIR, - OpCode::GTRI, - OpCode::GTRR, - OpCode::EQIR, - OpCode::EQRI, - OpCode::EQRR, -]; - -#[derive(Debug)] -enum CPUErr { - InvalidRegister(i32), -} - -#[derive(Default)] -struct CPU { - registers: [i32; 6], -} - -impl CPU { - pub fn new() -> Self { - Default::default() - } - pub fn execute(&mut self, op: OpCode, var: &[i32]) -> Result { - use self::OpCode::*; - let res = match op { - ADDR => self.reg(var[0])? + self.reg(var[1])?, - ADDI => self.reg(var[0])? + var[1], - MULR => self.reg(var[0])? * self.reg(var[1])?, - MULI => self.reg(var[0])? * var[1], - BANR => self.reg(var[0])? & self.reg(var[1])?, - BANI => self.reg(var[0])? & var[1], - BORR => self.reg(var[0])? | self.reg(var[1])?, - BORI => self.reg(var[0])? | var[1], - SETR => self.reg(var[0])?, - SETI => var[0], - GTRR => (self.reg(var[0])? > self.reg(var[1])?).into(), - GTIR => (var[0] > self.reg(var[1])?).into(), - GTRI => (self.reg(var[0])? > var[1]).into(), - EQRR => (self.reg(var[0])? == self.reg(var[1])?).into(), - EQIR => (var[0] == self.reg(var[1])?).into(), - EQRI => (self.reg(var[0])? == var[1]).into(), - }; - - self.registers[var[2] as usize] = res; - Ok(res) - } - - fn reg(&self, index: i32) -> Result { - if let Some(val) = self.registers.get(index as usize) { - Ok(*val) - } else { - Err(CPUErr::InvalidRegister(index)) - } - } -} +use cpu::CPU; +use cpu::OpCode; #[derive(Default)] pub struct Day19 { @@ -170,7 +46,7 @@ impl Solution for Day19 { while (cpu.registers[self.ip] as usize) < self.program.len() { let (opcode, operands) = &self.program[cpu.registers[self.ip] as usize]; - cpu.execute(*opcode, operands); + cpu.execute(*opcode, operands).unwrap(); cpu.registers[self.ip] += 1; } @@ -195,7 +71,7 @@ impl Solution for Day19 { reg[3] = 12; } let (opcode, operands) = &self.program[cpu.registers[self.ip] as usize]; - cpu.execute(*opcode, operands); + cpu.execute(*opcode, operands).unwrap(); cpu.registers[self.ip] += 1; } @@ -205,14 +81,14 @@ impl Solution for Day19 { #[cfg(test)] mod tests { - use day19::Day19; use common::Solution; + use day19::Day19; const SAMPLE_INPUT: &[u8] = include_bytes!("samples/19.txt"); #[test] fn sample_part1() { let mut instance = Day19::new(); - assert_eq!("6", instance.part1(&mut SAMPLE_INPUT)); + assert_eq!("7", instance.part1(&mut SAMPLE_INPUT)); } } diff --git a/2018/src/lib.rs b/2018/src/lib.rs index 017ae87..7a9f641 100644 --- a/2018/src/lib.rs +++ b/2018/src/lib.rs @@ -4,6 +4,7 @@ extern crate itertools; extern crate regex; pub mod common; +pub mod cpu; pub mod day01; pub mod day02; pub mod day03;