summaryrefslogtreecommitdiff
path: root/2018/src/bin/day_16.rs
diff options
context:
space:
mode:
Diffstat (limited to '2018/src/bin/day_16.rs')
-rw-r--r--2018/src/bin/day_16.rs205
1 files changed, 205 insertions, 0 deletions
diff --git a/2018/src/bin/day_16.rs b/2018/src/bin/day_16.rs
new file mode 100644
index 0000000..275ec45
--- /dev/null
+++ b/2018/src/bin/day_16.rs
@@ -0,0 +1,205 @@
+extern crate advent_of_code_2018;
+use advent_of_code_2018::*;
+
+use std::error::Error;
+use std::path::PathBuf;
+
+use std::collections::{HashMap, HashSet};
+
+// cargo watch -cs "cargo run --release --bin day_16"
+
+struct Instruction {
+ op: Op,
+ a: i32,
+ b: i32,
+ c: i32
+}
+
+impl Instruction {
+ fn execute(&self, registers: &[i32; 4]) -> [i32; 4] {
+ use Op::*;
+
+ let mut result_registers = registers.clone();
+
+ result_registers[self.c as usize] = match self.op {
+ Addr => registers[self.a as usize] + registers[self.b as usize],
+ Addi => registers[self.a as usize] + self.b,
+ Mulr => registers[self.a as usize] * registers[self.b as usize],
+ Muli => registers[self.a as usize] * self.b,
+ Banr => registers[self.a as usize] & registers[self.b as usize],
+ Bani => registers[self.a as usize] & self.b,
+ Borr => registers[self.a as usize] | registers[self.b as usize],
+ Bori => registers[self.a as usize] | self.b,
+ Setr => registers[self.a as usize],
+ Seti => self.a,
+ Gtir => if self.a > registers[self.b as usize] { 1 } else { 0 },
+ Gtri => if registers[self.a as usize] > self.b { 1 } else { 0 },
+ Gtrr => if registers[self.a as usize] > registers[self.b as usize] { 1 } else { 0 },
+ Eqir => if self.a == registers[self.b as usize] { 1 } else { 0 },
+ Eqri => if registers[self.a as usize] == self.b { 1 } else { 0 },
+ Eqrr => if registers[self.a as usize] == registers[self.b as usize] { 1 } else { 0 }
+ };
+
+ result_registers
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+enum Op {
+ Addr,
+ Addi,
+ Mulr,
+ Muli,
+ Banr,
+ Bani,
+ Borr,
+ Bori,
+ Setr,
+ Seti,
+ Gtir,
+ Gtri,
+ Gtrr,
+ Eqir,
+ Eqri,
+ Eqrr
+}
+
+impl Op {
+ fn all() -> [Op; 16] {
+ use Op::*;
+ [
+ Addr,
+ Addi,
+ Mulr,
+ Muli,
+ Banr,
+ Bani,
+ Borr,
+ Bori,
+ Setr,
+ Seti,
+ Gtir,
+ Gtri,
+ Gtrr,
+ Eqir,
+ Eqri,
+ Eqrr
+ ]
+ }
+}
+
+struct UnknownInstruction {
+ before: [i32; 4],
+ after: [i32; 4],
+ opcode: i32,
+ a: i32,
+ b: i32,
+ c: i32
+}
+
+impl UnknownInstruction {
+ fn possible_matches(&self) -> HashSet<Op> {
+ Op::all()
+ .iter()
+ .filter(|&&op| {
+ let instruction = Instruction {
+ op: op,
+ a: self.a,
+ b: self.b,
+ c: self.c
+ };
+ let result = instruction.execute(&self.before);
+ result == self.after
+ })
+ .cloned()
+ .collect()
+ }
+}
+
+fn main() -> Result<(), Box<Error>> {
+ let input_part_1 = read_file(&PathBuf::from("inputs/16_1.txt"))?;
+
+ let unknown_instructions: Vec<UnknownInstruction> = input_part_1.chunks(3)
+ .map(|chunk| {
+ let mut before_iter = chunk[0].trim_matches(|c: char| !c.is_numeric()).split(", ").map(|c| c.parse::<i32>().unwrap());
+ let before = [
+ before_iter.next().unwrap(),
+ before_iter.next().unwrap(),
+ before_iter.next().unwrap(),
+ before_iter.next().unwrap(),
+ ];
+ let mut after_iter = chunk[2].trim_matches(|c: char| !c.is_numeric()).split(", ").map(|c| c.parse::<i32>().unwrap());
+ let after = [
+ after_iter.next().unwrap(),
+ after_iter.next().unwrap(),
+ after_iter.next().unwrap(),
+ after_iter.next().unwrap(),
+ ];
+ let mut instruction_iter = chunk[1].split_whitespace().map(|c| c.parse::<i32>().unwrap());
+ UnknownInstruction {
+ before, after,
+ opcode: instruction_iter.next().unwrap(),
+ a: instruction_iter.next().unwrap(),
+ b: instruction_iter.next().unwrap(),
+ c: instruction_iter.next().unwrap(),
+ }
+ })
+ .collect();
+
+ let matches_more_then_3 = unknown_instructions.iter()
+ .filter(|unknown| {
+ unknown.possible_matches().len() >= 3
+ })
+ .count();
+
+ debug!(matches_more_then_3);
+
+
+ let mut opcodes: HashMap<i32, HashSet<Op>> = HashMap::new();
+ for unknown in unknown_instructions {
+ let matches = unknown.possible_matches();
+ let to_insert = match opcodes.get(&unknown.opcode) {
+ None => matches,
+ Some(existing) => existing.intersection(&matches).cloned().collect()
+ };
+ opcodes.insert(unknown.opcode, to_insert);
+ }
+ debug!(opcodes);
+
+ let mut known_opcodes: HashMap<i32, Op> = HashMap::new();
+
+ while known_opcodes.len() < 16 {
+ let (opcode, op) = {
+ let (opcode, opset) = opcodes.iter().find(|(_,set)| set.len() == 1).unwrap();
+ let op = opset.iter().next().unwrap().clone();
+ (opcode.clone(), op)
+ };
+ known_opcodes.insert(opcode, op);
+ opcodes.iter_mut().for_each(|(_, set)| {
+ set.remove(&op);
+ });
+ }
+ debug!(known_opcodes);
+
+
+ let input_part_2 = read_file(&PathBuf::from("inputs/16_2.txt"))?;
+ let instructions: Vec<Instruction> = input_part_2.iter()
+ .map(|line| {
+ let mut instruction_iter = line.split_whitespace().map(|c| c.parse::<i32>().unwrap());
+ Instruction {
+ op: known_opcodes.get(&instruction_iter.next().unwrap()).unwrap().clone(),
+ a: instruction_iter.next().unwrap(),
+ b: instruction_iter.next().unwrap(),
+ c: instruction_iter.next().unwrap(),
+ }
+ })
+ .collect();
+
+ let mut registers = [0; 4];
+ for instruction in instructions {
+ registers = instruction.execute(&registers);
+ }
+ debug!(registers);
+
+ Ok(())
+}