From cb99849143222a638aa1cbdd9f18c54d5a090181 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 19 Dec 2018 07:12:46 +0100 Subject: [PATCH] Implement day 19. With nice hotspot optimization for part 2. --- 2018/inputs/19.txt | 37 ++++++++ 2018/src/day19.rs | 205 ++++++++++++++++++++++++++++++++++++++-- 2018/src/samples/19.txt | 8 ++ 3 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 2018/inputs/19.txt create mode 100644 2018/src/samples/19.txt diff --git a/2018/inputs/19.txt b/2018/inputs/19.txt new file mode 100644 index 0000000..36ce063 --- /dev/null +++ b/2018/inputs/19.txt @@ -0,0 +1,37 @@ +#ip 3 +addi 3 16 3 +seti 1 8 1 +seti 1 3 4 +mulr 1 4 2 +eqrr 2 5 2 +addr 2 3 3 +addi 3 1 3 +addr 1 0 0 +addi 4 1 4 +gtrr 4 5 2 +addr 3 2 3 +seti 2 6 3 +addi 1 1 1 +gtrr 1 5 2 +addr 2 3 3 +seti 1 5 3 +mulr 3 3 3 +addi 5 2 5 +mulr 5 5 5 +mulr 3 5 5 +muli 5 11 5 +addi 2 5 2 +mulr 2 3 2 +addi 2 21 2 +addr 5 2 5 +addr 3 0 3 +seti 0 4 3 +setr 3 1 2 +mulr 2 3 2 +addr 3 2 2 +mulr 3 2 2 +muli 2 14 2 +mulr 2 3 2 +addr 5 2 5 +seti 0 3 0 +seti 0 6 3 diff --git a/2018/src/day19.rs b/2018/src/day19.rs index e8ea334..558e878 100644 --- a/2018/src/day19.rs +++ b/2018/src/day19.rs @@ -1,25 +1,218 @@ 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)] -pub struct Day19 {} +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)) + } + } +} + +#[derive(Default)] +pub struct Day19 { + program: Vec<(OpCode, [i32; 3])>, + ip: usize, +} impl Day19 { pub fn new() -> Self { Default::default() } + + fn read_input(&mut self, input: &mut Read) { + let reader = BufReader::new(input); + + for line in reader.lines() { + let line = line.unwrap(); + if line.chars().next().unwrap() == '#' { + self.ip = line.split(' ').last().unwrap().parse().unwrap(); + } else { + let mut parts = line.split(' '); + let opcode = OpCode::from(parts.next().unwrap()); + let mut operands = [0; 3]; + for i in 0..3 { + operands[i] = parts.next().unwrap().parse().unwrap(); + } + + self.program.push((opcode, operands)); + } + } + } } impl Solution for Day19 { - fn part1(&mut self, _input: &mut Read) -> String { - unimplemented!() + fn part1(&mut self, input: &mut Read) -> String { + self.read_input(input); + + let mut cpu = CPU::new(); + + 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.registers[self.ip] += 1; + } + + format!("{}", cpu.registers[0]) } - fn part2(&mut self, _input: &mut Read) -> String { - unimplemented!() + fn part2(&mut self, input: &mut Read) -> String { + self.read_input(input); + + let mut cpu = CPU::new(); + cpu.registers[0] = 1; + + // This is optimized for my input. + assert_eq!(self.ip, 3); + + while (cpu.registers[3] as usize) < self.program.len() { + if cpu.registers[3] == 3 { + let reg = &mut cpu.registers; + if reg[5] % reg[1] == 0 { + reg[0] += reg[1]; + } + reg[3] = 12; + } + let (opcode, operands) = &self.program[cpu.registers[self.ip] as usize]; + cpu.execute(*opcode, operands); + cpu.registers[self.ip] += 1; + } + + format!("{}", cpu.registers[0]) } } #[cfg(test)] -mod tests {} +mod tests { + use day19::Day19; + use common::Solution; + + 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)); + } +} diff --git a/2018/src/samples/19.txt b/2018/src/samples/19.txt new file mode 100644 index 0000000..d7d2a21 --- /dev/null +++ b/2018/src/samples/19.txt @@ -0,0 +1,8 @@ +#ip 0 +seti 5 0 1 +seti 6 0 2 +addi 0 1 0 +addr 1 2 3 +setr 1 0 0 +seti 8 0 4 +seti 9 0 5