Clean up day 16 and 19.

Day 16 and 19 share the same CPU and now they actually share the same
code for it.
This commit is contained in:
2018-12-19 07:53:09 +01:00
parent cb99849143
commit c554811fda
4 changed files with 197 additions and 251 deletions

167
2018/src/cpu.rs Normal file
View File

@@ -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<Item=Self> {
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<i32, CPUErr> {
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<i32, CPUErr> {
if let Some(val) = self.registers.get(index as usize) {
Ok(*val)
} else {
Err(CPUErr::InvalidRegister(index))
}
}
}

View File

@@ -6,105 +6,8 @@ use std::io::Read;
use regex::Regex; use regex::Regex;
use common::Solution; use common::Solution;
use cpu::OpCode;
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] use cpu::CPU;
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<i32, CPUErr> {
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<i32, CPUErr> {
if let Some(val) = self.registers.get(index as usize) {
Ok(*val)
} else {
Err(CPUErr::InvalidRegister(index))
}
}
}
pub struct Day16 { pub struct Day16 {
matcher: Regex, 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(); self.buf.clear();
if reader.read_line(&mut self.buf).is_err() { if reader.read_line(&mut self.buf).is_err() {
return false; return false;
} }
if let Some(captures) = self.matcher.captures(&self.buf) { if let Some(captures) = self.matcher.captures(&self.buf) {
for i in 0..4 { for (target, cap) in target.iter_mut().zip(captures.iter().skip(1)) {
target[i] = captures[i + 1].parse().unwrap(); *target = cap.unwrap().as_str().parse().unwrap();
} }
true true
@@ -143,22 +46,21 @@ impl Day16 {
HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new(),
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 op = [0; 4];
let mut after = [0; 4]; let mut after = [0; 6];
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 op);
self.read(&mut reader, &mut after); self.read(&mut reader, &mut after[..4]);
reader.read_line(&mut self.buf).unwrap_or(0); reader.read_line(&mut self.buf).unwrap_or(0);
if mappings[op[0] as usize].is_empty() { if mappings[op[0] as usize].is_empty() {
mappings[op[0] as usize].extend(OP_LIST.iter() mappings[op[0] as usize].extend(OpCode::values()
.filter(|x| x.valid(&op, &before, &after)) .filter(|x| x.is_valid(&op, &before, &after)));
.cloned());
} else { } else {
for option in OP_LIST.iter() for option in OpCode::values()
.filter(|x| !x.valid(&op, &before, &after)) { .filter(|x| !x.is_valid(&op, &before, &after)) {
mappings[op[0] as usize].remove(option); mappings[op[0] as usize].remove(&option);
} }
continue; continue;
} }
@@ -207,18 +109,18 @@ impl Solution for Day16 {
fn part1(&mut self, input: &mut Read) -> String { fn part1(&mut self, input: &mut Read) -> String {
let mut reader = BufReader::new(input); let mut reader = BufReader::new(input);
let mut before = [0; 4]; let mut before = [0; 6];
let mut op = [0; 4]; let mut op = [0; 4];
let mut after = [0; 4]; let mut after = [0; 6];
let mut counter = 0; 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 op);
self.read(&mut reader, &mut after); self.read(&mut reader, &mut after[..4]);
reader.read_line(&mut self.buf).unwrap_or(0); reader.read_line(&mut self.buf).unwrap_or(0);
let valid = OP_LIST.iter() let valid = OpCode::values()
.filter(|x| x.valid(&op, &before, &after)) .filter(|x| x.is_valid(&op, &before, &after))
.count(); .count();
if valid >= 3 { if valid >= 3 {
@@ -238,7 +140,7 @@ impl Solution for Day16 {
// Skip a line // Skip a line
reader.read_line(&mut self.buf).unwrap(); 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) { while self.read(&mut reader, &mut op) {
cpu.execute(mapping[op[0] as usize], &op[1..4]).unwrap(); cpu.execute(mapping[op[0] as usize], &op[1..4]).unwrap();

View File

@@ -1,134 +1,10 @@
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read; use std::io::Read;
use common::Solution; use common::Solution;
use std::io::BufReader; use cpu::CPU;
use std::io::BufRead; use cpu::OpCode;
#[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<i32, CPUErr> {
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<i32, CPUErr> {
if let Some(val) = self.registers.get(index as usize) {
Ok(*val)
} else {
Err(CPUErr::InvalidRegister(index))
}
}
}
#[derive(Default)] #[derive(Default)]
pub struct Day19 { pub struct Day19 {
@@ -170,7 +46,7 @@ impl Solution for Day19 {
while (cpu.registers[self.ip] as usize) < self.program.len() { while (cpu.registers[self.ip] as usize) < self.program.len() {
let (opcode, operands) = &self.program[cpu.registers[self.ip] as usize]; 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; cpu.registers[self.ip] += 1;
} }
@@ -195,7 +71,7 @@ impl Solution for Day19 {
reg[3] = 12; reg[3] = 12;
} }
let (opcode, operands) = &self.program[cpu.registers[self.ip] as usize]; 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; cpu.registers[self.ip] += 1;
} }
@@ -205,14 +81,14 @@ impl Solution for Day19 {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use day19::Day19;
use common::Solution; use common::Solution;
use day19::Day19;
const SAMPLE_INPUT: &[u8] = include_bytes!("samples/19.txt"); const SAMPLE_INPUT: &[u8] = include_bytes!("samples/19.txt");
#[test] #[test]
fn sample_part1() { fn sample_part1() {
let mut instance = Day19::new(); let mut instance = Day19::new();
assert_eq!("6", instance.part1(&mut SAMPLE_INPUT)); assert_eq!("7", instance.part1(&mut SAMPLE_INPUT));
} }
} }

View File

@@ -4,6 +4,7 @@ extern crate itertools;
extern crate regex; extern crate regex;
pub mod common; pub mod common;
pub mod cpu;
pub mod day01; pub mod day01;
pub mod day02; pub mod day02;
pub mod day03; pub mod day03;