Day 16 part 2.

This commit is contained in:
2023-11-12 19:15:31 +01:00
parent 8e1f42205c
commit 42ead11aa0
2 changed files with 76 additions and 4 deletions

View File

@@ -1,3 +1,3 @@
fn main() { fn main() {
aoc_2018::tasks::day16::task1(); aoc_2018::tasks::day16::task2();
} }

View File

@@ -1,3 +1,4 @@
use std::collections::{HashMap, HashSet};
use std::ops::{BitAnd, BitOr}; use std::ops::{BitAnd, BitOr};
use itertools::Itertools; use itertools::Itertools;
@@ -15,6 +16,65 @@ pub fn task1() {
println!("Day 16 part1: {count}"); println!("Day 16 part1: {count}");
} }
pub fn task2() {
let (samples, program) = include_str!("../../input/day16.txt")
.split_once("\n\n\n\n")
.unwrap();
let code_to_op_candidates = samples
.split("\n\n")
.map(|sample_input| parse(sample_input))
.map(|sample| (sample.1.opcode, matches_ops(sample)))
.into_group_map_by(|x| x.0);
// let code_to_op: HashMap<usize, HashSet<Op>> =
let mut code_to_op_candidates: HashMap<_, _> = code_to_op_candidates
.into_iter()
.map(|(code, ops)| {
let mut set: HashSet<_> = HashSet::from_iter(OPS.iter().map(ToOwned::to_owned));
for (_, candidate_set) in ops {
set = set
.intersection(&candidate_set)
.map(ToOwned::to_owned)
.collect();
}
(code, set)
})
.collect();
let mut codes_to_op: HashMap<usize, Op> = HashMap::new();
loop {
let single = code_to_op_candidates.iter().find(|(_, ops)| ops.len() == 1);
let Some(single) = single else {
break;
};
let code = *single.0;
let op = *single.1.iter().next().unwrap();
codes_to_op.insert(code, op);
code_to_op_candidates.iter_mut().for_each(|(_, ops)| {
ops.remove(&op);
});
}
if codes_to_op.len() != OPS.len() {
panic!("Bad final opcode assignments: {codes_to_op:?}");
}
let program: Vec<Instruction> = program.lines().map(parse_instruction).collect();
let mut registers = Registers([0, 0, 0, 0]);
for instruction in program {
let op: Op = codes_to_op[&instruction.opcode];
registers = op.process(registers, instruction);
}
println!("Day 16 part 2: {}", registers.0[0]);
}
fn parse(sample: &str) -> Sample { fn parse(sample: &str) -> Sample {
let (before, instruction, after) = sample.lines().collect_tuple().unwrap(); let (before, instruction, after) = sample.lines().collect_tuple().unwrap();
let before: Vec<_> = before let before: Vec<_> = before
@@ -29,6 +89,12 @@ fn parse(sample: &str) -> Sample {
.split(", ") .split(", ")
.collect(); .collect();
let after = Registers::from(after); let after = Registers::from(after);
let instruction = parse_instruction(instruction);
Sample(before, instruction, after)
}
fn parse_instruction(instruction: &str) -> Instruction {
let instruction: Vec<usize> = instruction.split(" ").map(|x| x.parse().unwrap()).collect(); let instruction: Vec<usize> = instruction.split(" ").map(|x| x.parse().unwrap()).collect();
let instruction = Instruction { let instruction = Instruction {
opcode: instruction[0], opcode: instruction[0],
@@ -36,8 +102,7 @@ fn parse(sample: &str) -> Sample {
b: instruction[2], b: instruction[2],
c: instruction[3], c: instruction[3],
}; };
instruction
Sample(before, instruction, after)
} }
fn matches(sample: Sample, op: Op) -> bool { fn matches(sample: Sample, op: Op) -> bool {
@@ -49,6 +114,13 @@ fn matches_count(sample: Sample) -> usize {
vec.len() vec.len()
} }
fn matches_ops(sample: Sample) -> HashSet<Op> {
OPS.iter()
.filter(|op| matches(sample, **op))
.map(|op| op.to_owned())
.collect()
}
const OPS: &[Op] = &[ const OPS: &[Op] = &[
Op::AddR, Op::AddR,
Op::AddI, Op::AddI,
@@ -68,7 +140,7 @@ const OPS: &[Op] = &[
Op::EqRR, Op::EqRR,
]; ];
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
enum Op { enum Op {
AddR, AddR,
AddI, AddI,