Skip to content

Commit 4a9b2f7

Browse files
committed
2016 day 25
1 parent 6a78c31 commit 4a9b2f7

File tree

5 files changed

+229
-111
lines changed

5 files changed

+229
-111
lines changed

crates/year2016/src/assembunny.rs

Lines changed: 152 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
//! Assembunny interpreter.
22
//!
3-
//! See [`Day12`](crate::Day12) and [`Day23`](crate::Day23).
3+
//! See [`Day12`](crate::Day12), [`Day23`](crate::Day23) and [`Day25`](crate::Day25).
44
5-
use std::marker::PhantomData;
5+
use std::ops::ControlFlow;
66
use utils::prelude::*;
77

8-
pub(crate) trait InterpreterConfig {
9-
const SUPPORTS_TOGGLE: bool = false;
10-
}
11-
128
#[derive(Clone, Debug)]
13-
pub(crate) struct Interpreter<C: InterpreterConfig> {
9+
pub(crate) struct Interpreter<const TGL: bool, const OUT: bool> {
1410
instructions: Vec<Instruction>,
15-
phantom: PhantomData<C>,
1611
}
1712

1813
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -36,10 +31,11 @@ enum Instruction {
3631
Decrement(Register),
3732
JumpIfNotZero(Value, Value),
3833
Toggle(Register),
34+
Out(Register),
3935
Invalid2(Value, Value),
4036
}
4137

42-
impl<C: InterpreterConfig> Interpreter<C> {
38+
impl<const TGL: bool, const OUT: bool> Interpreter<TGL, OUT> {
4339
pub fn new(input: &str) -> Result<Self, InputError> {
4440
let register = parser::one_of((
4541
b'a'.map(|_| Register::A),
@@ -64,117 +60,172 @@ impl<C: InterpreterConfig> Interpreter<C> {
6460
.then(value.with_prefix(" "))
6561
.map(|(v, o)| Instruction::JumpIfNotZero(v, o)),
6662
register.with_prefix("tgl ").map_res(|r| {
67-
if C::SUPPORTS_TOGGLE {
63+
if TGL {
6864
Ok(Instruction::Toggle(r))
6965
} else {
70-
Err("toggle instruction not supported")
66+
Err("tgl instruction not supported")
67+
}
68+
}),
69+
register.with_prefix("out ").map_res(|r| {
70+
if OUT {
71+
Ok(Instruction::Out(r))
72+
} else {
73+
Err("out instruction not supported")
7174
}
7275
}),
7376
))
7477
.parse_lines(input)?,
75-
phantom: PhantomData,
7678
})
7779
}
80+
}
81+
82+
impl<const TGL: bool> Interpreter<TGL, false> {
83+
pub fn execute(&self, reg: [i32; 4]) -> i32 {
84+
execute(&self.instructions, reg, |_, _| unreachable!())
85+
}
86+
}
87+
88+
impl<const TGL: bool> Interpreter<TGL, true> {
89+
pub fn execute(
90+
&self,
91+
reg: [i32; 4],
92+
out_fn: impl FnMut(i32, (usize, [i32; 4])) -> ControlFlow<()>,
93+
) -> i32 {
94+
execute(&self.instructions, reg, out_fn)
95+
}
96+
}
97+
98+
#[inline]
99+
fn execute(
100+
instructions: &[Instruction],
101+
mut reg: [i32; 4],
102+
mut out_fn: impl FnMut(i32, (usize, [i32; 4])) -> ControlFlow<()>,
103+
) -> i32 {
104+
let mut pc = 0;
105+
106+
let mut instructions = instructions.to_vec();
107+
while pc < instructions.len() {
108+
#[rustfmt::skip] // Rustfmt wants each pattern to be on a single really long line
109+
match instructions[pc..] {
110+
// Recognize the following pattern of instructions which can be simplified to addition
111+
// inc $x
112+
// dec $y
113+
// jnz $y -2
114+
// This is the key optimization for Day 12
115+
[
116+
Instruction::Increment(x),
117+
Instruction::Decrement(y),
118+
Instruction::JumpIfNotZero(Value::Register(y2), Value::Number(-2)),
119+
..
120+
] if y == y2 => {
121+
reg[x as usize] += reg[y as usize];
122+
reg[y as usize] = 0;
123+
pc += 3;
124+
continue;
125+
}
126+
// Recognize the following pattern of instructions which can be simplified to multiplication
127+
// cpy $w $x
128+
// inc $y
129+
// dec $x
130+
// jnz $x -2
131+
// dec $z
132+
// jnz $z -5
133+
// This is the key optimisation for Day 23
134+
[
135+
Instruction::Copy(Value::Register(w), x),
136+
Instruction::Increment(y),
137+
Instruction::Decrement(x2),
138+
Instruction::JumpIfNotZero(Value::Register(x3), Value::Number(-2)),
139+
Instruction::Decrement(z),
140+
Instruction::JumpIfNotZero(Value::Register(z2), Value::Number(-5)),
141+
..
142+
] if x == x2 && x == x3 && z == z2 => {
143+
reg[y as usize] = reg[w as usize] * reg[z as usize];
144+
reg[x as usize] = 0;
145+
reg[z as usize] = 0;
146+
pc += 6;
147+
continue;
148+
}
149+
// Recognize the following pattern of instructions which can be simplified to division
150+
// cpy $N $x
151+
// jnz $y 2
152+
// jnz 1 6
153+
// dec $y
154+
// dec $x
155+
// jnz $x -4
156+
// inc $z
157+
// jnz 1 -7
158+
// This is the key optimisation for Day 25
159+
[
160+
Instruction::Copy(Value::Number(n), x),
161+
Instruction::JumpIfNotZero(Value::Register(y), Value::Number(2)),
162+
Instruction::JumpIfNotZero(Value::Number(1), Value::Number(6)),
163+
Instruction::Decrement(y2),
164+
Instruction::Decrement(x2),
165+
Instruction::JumpIfNotZero(Value::Register(x3), Value::Number(-4)),
166+
Instruction::Increment(z),
167+
Instruction::JumpIfNotZero(Value::Number(1), Value::Number(-7)),
168+
..
169+
] if x == x2 && x == x3 && y == y2 => {
170+
reg[z as usize] += reg[y as usize] / n;
171+
reg[x as usize] = n - (reg[y as usize] % n);
172+
reg[y as usize] = 0;
173+
pc += 8;
174+
continue;
175+
}
176+
_ => {}
177+
};
78178

79-
pub fn execute(&self, mut reg: [i32; 4]) -> i32 {
80-
let mut pc = 0;
81-
82-
let mut instructions = self.instructions.clone();
83-
while pc < instructions.len() {
84-
#[rustfmt::skip] // Rustfmt wants each pattern to be on a single really long line
85-
match instructions[pc..] {
86-
// Recognize the following pattern of instructions which can be simplified to addition
87-
// inc $x
88-
// dec $y
89-
// jnz $y -2
90-
// This is the key optimization for Day 12
91-
[
92-
Instruction::Increment(x),
93-
Instruction::Decrement(y),
94-
Instruction::JumpIfNotZero(Value::Register(y2), Value::Number(-2)),
95-
..
96-
] if y == y2 => {
97-
reg[x as usize] += reg[y as usize];
98-
reg[y as usize] = 0;
99-
pc += 3;
179+
match instructions[pc] {
180+
Instruction::Copy(v, dst) => reg[dst as usize] = v.get(&reg),
181+
Instruction::Increment(dst) => reg[dst as usize] += 1,
182+
Instruction::Decrement(dst) => reg[dst as usize] -= 1,
183+
Instruction::JumpIfNotZero(v, offset) => {
184+
if v.get(&reg) != 0 {
185+
let offset = offset.get(&reg);
186+
let Some(new_pc) = pc.checked_add_signed(offset as isize) else {
187+
break;
188+
};
189+
pc = new_pc;
100190
continue;
101191
}
102-
// Recognize the following pattern of instructions which can be simplified to multiplication
103-
// cpy $w $x
104-
// inc $y
105-
// dec $x
106-
// jnz $x -2
107-
// dec $z
108-
// jnz $z -5
109-
// This is the key optimisation for Day 23
110-
[
111-
Instruction::Copy(Value::Register(w), x),
112-
Instruction::Increment(y),
113-
Instruction::Decrement(x2),
114-
Instruction::JumpIfNotZero(Value::Register(x3), Value::Number(-2)),
115-
Instruction::Decrement(z),
116-
Instruction::JumpIfNotZero(Value::Register(z2), Value::Number(-5)),
117-
..
118-
] if x == x2 && x == x3 && z == z2 => {
119-
reg[y as usize] = reg[w as usize] * reg[z as usize];
120-
reg[x as usize] = 0;
121-
reg[z as usize] = 0;
122-
pc += 6;
123-
continue;
192+
}
193+
Instruction::Toggle(r) => 'toggle: {
194+
let Some(index) = pc.checked_add_signed(reg[r as usize] as isize) else {
195+
break 'toggle;
196+
};
197+
if index >= instructions.len() {
198+
break 'toggle;
124199
}
125-
_ => {}
126-
};
127-
128-
match instructions[pc] {
129-
Instruction::Copy(v, dst) => reg[dst as usize] = v.get(&reg),
130-
Instruction::Increment(dst) => reg[dst as usize] += 1,
131-
Instruction::Decrement(dst) => reg[dst as usize] -= 1,
132-
Instruction::JumpIfNotZero(v, offset) => {
133-
if v.get(&reg) != 0 {
134-
let offset = offset.get(&reg);
135-
let Some(new_pc) = pc.checked_add_signed(offset as isize) else {
136-
break;
137-
};
138-
pc = new_pc;
139-
continue;
200+
201+
instructions[index] = match instructions[index] {
202+
Instruction::Increment(r) => Instruction::Decrement(r),
203+
Instruction::Decrement(r) => Instruction::Increment(r),
204+
Instruction::Toggle(r) => Instruction::Increment(r),
205+
Instruction::Out(r) => Instruction::Increment(r),
206+
Instruction::JumpIfNotZero(v, Value::Register(r)) => Instruction::Copy(v, r),
207+
Instruction::JumpIfNotZero(v, o @ Value::Number(_)) => {
208+
Instruction::Invalid2(v, o)
140209
}
141-
}
142-
Instruction::Toggle(r) => 'toggle: {
143-
let Some(index) = pc.checked_add_signed(reg[r as usize] as isize) else {
144-
break 'toggle;
145-
};
146-
if index >= instructions.len() {
147-
break 'toggle;
210+
Instruction::Copy(v, r) => Instruction::JumpIfNotZero(v, Value::Register(r)),
211+
Instruction::Invalid2(v1, v2) => {
212+
// Effectively an invalid copy instruction
213+
Instruction::JumpIfNotZero(v1, v2)
148214
}
149-
150-
instructions[index] = match instructions[index] {
151-
Instruction::Increment(r) => Instruction::Decrement(r),
152-
Instruction::Decrement(r) | Instruction::Toggle(r) => {
153-
Instruction::Increment(r)
154-
}
155-
Instruction::JumpIfNotZero(v, Value::Register(r)) => {
156-
Instruction::Copy(v, r)
157-
}
158-
Instruction::JumpIfNotZero(v, o @ Value::Number(_)) => {
159-
Instruction::Invalid2(v, o)
160-
}
161-
Instruction::Copy(v, r) => {
162-
Instruction::JumpIfNotZero(v, Value::Register(r))
163-
}
164-
Instruction::Invalid2(v1, v2) => {
165-
// Effectively an invalid copy instruction
166-
Instruction::JumpIfNotZero(v1, v2)
167-
}
168-
};
215+
};
216+
}
217+
Instruction::Out(r) => {
218+
if out_fn(reg[r as usize], (pc, reg)).is_break() {
219+
break;
169220
}
170-
Instruction::Invalid2(_, _) => {}
171221
}
172-
173-
pc += 1;
222+
Instruction::Invalid2(_, _) => {}
174223
}
175224

176-
reg[0]
225+
pc += 1;
177226
}
227+
228+
reg[0]
178229
}
179230

180231
impl Value {

crates/year2016/src/day12.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::assembunny::{Interpreter, InterpreterConfig};
1+
use crate::assembunny::Interpreter;
22
use utils::prelude::*;
33

44
/// Interpreting assembly, again.
@@ -13,7 +13,7 @@ use utils::prelude::*;
1313
/// cycles ~5,000 times for part 1 and ~100,000 times for part 2, to around ~200 cycles each.
1414
#[derive(Clone, Debug)]
1515
pub struct Day12 {
16-
interpreter: Interpreter<Self>,
16+
interpreter: Interpreter<false, false>,
1717
}
1818

1919
impl Day12 {
@@ -34,8 +34,6 @@ impl Day12 {
3434
}
3535
}
3636

37-
impl InterpreterConfig for Day12 {}
38-
3937
examples!(Day12 -> (i32, i32) [
4038
{
4139
input: "cpy 41 a\n\

crates/year2016/src/day23.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::assembunny::{Interpreter, InterpreterConfig};
1+
use crate::assembunny::Interpreter;
22
use utils::prelude::*;
33

44
/// Interpreting assembunny assembly, again.
@@ -19,7 +19,7 @@ use utils::prelude::*;
1919
/// See also [2016 day 12](crate::Day12).
2020
#[derive(Clone, Debug)]
2121
pub struct Day23 {
22-
interpreter: Interpreter<Self>,
22+
interpreter: Interpreter<true, false>,
2323
}
2424

2525
impl Day23 {
@@ -40,10 +40,6 @@ impl Day23 {
4040
}
4141
}
4242

43-
impl InterpreterConfig for Day23 {
44-
const SUPPORTS_TOGGLE: bool = true;
45-
}
46-
4743
examples!(Day23 -> (i32, i32) [
4844
{
4945
input: "cpy 2 a\n\

0 commit comments

Comments
 (0)