Skip to content

Commit

Permalink
Year 2018 Day 16
Browse files Browse the repository at this point in the history
  • Loading branch information
maneatingape committed Aug 23, 2024
1 parent 848dad5 commit 9bd6b44
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
| 13 | [Mine Cart Madness](https://adventofcode.com/2018/day/13) | [Source](src/year2018/day13.rs) | 391 |
| 14 | [Chocolate Charts](https://adventofcode.com/2018/day/14) | [Source](src/year2018/day14.rs) | 24000 |
| 15 | [Beverage Bandits](https://adventofcode.com/2018/day/15) | [Source](src/year2018/day15.rs) | 584 |
| 16 | [Chronal Classification](https://adventofcode.com/2018/day/16) | [Source](src/year2018/day16.rs) | 36 |

## 2017

Expand Down
1 change: 1 addition & 0 deletions benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ mod year2018 {
benchmark!(year2018, day13);
benchmark!(year2018, day14);
benchmark!(year2018, day15);
benchmark!(year2018, day16);
}

mod year2019 {
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub mod year2018 {
pub mod day13;
pub mod day14;
pub mod day15;
pub mod day16;
}

/// # Rescue Santa from deep space with a solar system voyage.
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ fn year2018() -> Vec<Solution> {
solution!(year2018, day13),
solution!(year2018, day14),
solution!(year2018, day15),
solution!(year2018, day16),
]
}

Expand Down
114 changes: 114 additions & 0 deletions src/year2018/day16.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//! # Chronal Classification
//!
//! There are only 16 opcodes so we can use bitwise logic to efficiently perform the set operations
//! that uniquely determine the mapping of each opcode to instruction.
//!
//! First we create a bitmask for each instruction block in the first half of the input
//! with a `1` for each potential instruction. For example:
//!
//! ```none
//! Before: [3, 2, 1, 1]
//! 9 2 1 2
//! After: [3, 2, 2, 1]
//!
//! Possible instructions: mulr, addi, seti
//! Binary Mask: 0000001000000110
//! ```
//!
//! For part one the [`count_ones`] intrinsic computes the size of each set.
//!
//! For part two we need to determine the mapping of the unknown codes. First we reduce each
//! unknown to a single set by taking the intersection of all examples. Then similar to
//! solving simultaneous equation, we eliminate one unknown at a time, removing it from the other
//! possibilities. This causes a domino effect, continuing until all unknowns are resolved.
//!
//! [`count_ones`]: u32::count_ones
use crate::util::iter::*;
use crate::util::parse::*;

pub struct Input {
samples: Vec<(usize, u32)>,
program: Vec<[usize; 4]>,
}

pub fn parse(input: &str) -> Input {
let (first, second) = input.rsplit_once("\n\n").unwrap();
let samples = first
.iter_unsigned()
.chunk::<4>()
.chunk::<3>()
.map(|[before, instruction, after]| {
let [unknown, a, b, c] = instruction;
let mut mask = 0;

// Build set of possible opcodes
for opcode in 0..16 {
if cpu(opcode, a, b, &before) == after[c] {
mask |= 1 << opcode;
}
}

(unknown, mask)
})
.collect();
let program = second.iter_unsigned().chunk::<4>().collect();

Input { samples, program }
}

pub fn part1(input: &Input) -> usize {
input.samples.iter().filter(|(_, mask)| mask.count_ones() >= 3).count()
}

pub fn part2(input: &Input) -> usize {
// Take intersection of samples, reducing each unknown opcode to a single set of possibilities.
let mut masks = [0xffff; 16];

for &(unknown, mask) in &input.samples {
masks[unknown] &= mask;
}

// To uniquely determine the mapping, there must be at least 1 opcode during each iteration
// that only has one possibility.
let mut convert = [0; 16];

while let Some(index) = masks.iter().position(|m| m.count_ones() == 1) {
let mask = masks[index];
// This opcode has only 1 possible mapping, so remove possbility from other opcodes.
masks.iter_mut().for_each(|m| *m &= !mask);
// Add mapping.
convert[index] = mask.trailing_zeros() as usize;
}

// Run the program now that we know the mapping.
let mut register = [0; 4];

for &[unknown, a, b, c] in &input.program {
let opcode = convert[unknown];
register[c] = cpu(opcode, a, b, &register);
}

register[0]
}

fn cpu(opcode: usize, a: usize, b: usize, register: &[usize; 4]) -> usize {
match opcode {
0 => register[a] + register[b], // addr
1 => register[a] + b, // addi
2 => register[a] * register[b], // mulr
3 => register[a] * b, // muli
4 => register[a] & register[b], // banr
5 => register[a] & b, // bani
6 => register[a] | register[b], // borr
7 => register[a] | b, // bori
8 => register[a], // setr
9 => a, // seti
10 => (a > register[b]) as usize, // gtir
11 => (register[a] > b) as usize, // gtri
12 => (register[a] > register[b]) as usize, // gtrr
13 => (a == register[b]) as usize, // eqir
14 => (register[a] == b) as usize, // eqri
15 => (register[a] == register[b]) as usize, // eqrr
_ => unreachable!(),
}
}
1 change: 1 addition & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ mod year2018 {
mod day13_test;
mod day14_test;
mod day15_test;
mod day16_test;
}

mod year2019 {
Expand Down
9 changes: 9 additions & 0 deletions tests/year2018/day16_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[test]
fn part1_test() {
// No example data
}

#[test]
fn part2_test() {
// No example data
}

0 comments on commit 9bd6b44

Please sign in to comment.