Skip to content

Commit c4aa844

Browse files
committed
2018 day 16
1 parent af95ace commit c4aa844

File tree

2 files changed

+133
-0
lines changed

2 files changed

+133
-0
lines changed

crates/year2018/src/day16.rs

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
use utils::prelude::*;
2+
3+
/// Inferring opcode operations from examples.
4+
#[derive(Clone, Debug)]
5+
pub struct Day16 {
6+
samples: Vec<(u32, u16)>,
7+
instructions: Vec<(u32, [u32; 3])>,
8+
}
9+
10+
utils::enumerable_enum! {
11+
#[repr(u32)]
12+
#[derive(Copy, Clone, Debug)]
13+
enum Operation {
14+
Addr,
15+
Addi,
16+
Mulr,
17+
Muli,
18+
Banr,
19+
Bani,
20+
Borr,
21+
Bori,
22+
Setr,
23+
Seti,
24+
Gtir,
25+
Gtri,
26+
Gtrr,
27+
Eqir,
28+
Eqri,
29+
Eqrr,
30+
}
31+
}
32+
33+
impl Day16 {
34+
pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
35+
let registers = parser::digit().map(u32::from).repeat_n::<4, _>(", ");
36+
let instruction = parser::number_range(0u32..=15)
37+
.with_suffix(b' ')
38+
.then(parser::number_range(0u32..=3).repeat_n::<3, _>(b' '));
39+
let sample = registers
40+
.with_prefix("Before: [")
41+
.with_suffix("]".then(parser::eol()))
42+
.then(instruction.with_suffix(parser::eol()))
43+
.then(
44+
registers
45+
.with_prefix("After: [")
46+
.with_suffix("]".then(parser::eol())),
47+
)
48+
.map(|(before, (opcode, [a, b, c]), after)| {
49+
(
50+
opcode,
51+
// Convert the before and after registers and the operands into a mask of
52+
// possible operations as used in both parts.
53+
Operation::iter().fold(0, |acc, op| {
54+
let possible = Self::execute(op, a, b, &before) == after[c as usize];
55+
acc | (possible as u16) << op as u32
56+
}),
57+
)
58+
});
59+
60+
let (samples, instructions) = sample
61+
.repeat(parser::eol(), 1)
62+
.with_eol()
63+
.with_eol()
64+
.with_eol()
65+
.then(instruction.repeat(parser::eol(), 1))
66+
.parse_complete(input)?;
67+
68+
Ok(Self {
69+
samples,
70+
instructions,
71+
})
72+
}
73+
74+
#[must_use]
75+
pub fn part1(&self) -> usize {
76+
self.samples
77+
.iter()
78+
.filter(|&(_, mask)| mask.count_ones() >= 3)
79+
.count()
80+
}
81+
82+
#[must_use]
83+
pub fn part2(&self) -> u32 {
84+
let mut op_masks = [u16::MAX; 16];
85+
for &(opcode, mask) in &self.samples {
86+
op_masks[opcode as usize] &= mask;
87+
}
88+
89+
let mut ops = [Operation::Addi; 16];
90+
for _ in 0..16 {
91+
let opcode = op_masks
92+
.iter()
93+
.position(|&mask| mask.count_ones() == 1)
94+
.expect("no solution found: all remaining opcodes could be multiple operations");
95+
96+
let mask = op_masks[opcode];
97+
ops[opcode] = Operation::from_discriminant(mask.trailing_zeros());
98+
99+
op_masks.iter_mut().for_each(|m| *m &= !mask);
100+
}
101+
102+
let mut registers = [0; 4];
103+
for &(opcode, [a, b, c]) in &self.instructions {
104+
registers[c as usize] = Self::execute(ops[opcode as usize], a, b, &registers);
105+
}
106+
registers[0]
107+
}
108+
109+
#[inline]
110+
fn execute(op: Operation, a: u32, b: u32, registers: &[u32; 4]) -> u32 {
111+
match op {
112+
Operation::Addr => registers[a as usize] + registers[b as usize],
113+
Operation::Addi => registers[a as usize] + b,
114+
Operation::Mulr => registers[a as usize] * registers[b as usize],
115+
Operation::Muli => registers[a as usize] * b,
116+
Operation::Banr => registers[a as usize] & registers[b as usize],
117+
Operation::Bani => registers[a as usize] & b,
118+
Operation::Borr => registers[a as usize] | registers[b as usize],
119+
Operation::Bori => registers[a as usize] | b,
120+
Operation::Setr => registers[a as usize],
121+
Operation::Seti => a,
122+
Operation::Gtir => u32::from(a > registers[b as usize]),
123+
Operation::Gtri => u32::from(registers[a as usize] > b),
124+
Operation::Gtrr => u32::from(registers[a as usize] > registers[b as usize]),
125+
Operation::Eqir => u32::from(a == registers[b as usize]),
126+
Operation::Eqri => u32::from(registers[a as usize] == b),
127+
Operation::Eqrr => u32::from(registers[a as usize] == registers[b as usize]),
128+
}
129+
}
130+
}
131+
132+
examples!(Day16 -> (usize, u32) []);

crates/year2018/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ utils::year!(2018 => year2018, ${
1717
13 => day13::Day13,
1818
14 => day14::Day14,
1919
15 => day15::Day15,
20+
16 => day16::Day16,
2021
});

0 commit comments

Comments
 (0)