1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//! # Opening the Turing Lock
//!
//! Reverse engineering the code shows that it calculates the length of the
//! [3n + 1 sequence](https://en.wikipedia.org/wiki/Collatz_conjecture)
//! for one of two different numbers chosen depending on whether `a` is 0 or 1.
//!
//! The code fast enough to emulate directly without needing any understanding of what it's doing.
use crate::util::parse::*;
pub enum Op {
Hlf,
Tpl,
IncA,
IncB,
Jmp(usize),
Jie(usize),
Jio(usize),
}
pub fn parse(input: &str) -> Vec<Op> {
input
.lines()
.enumerate()
.map(|(i, s)| match s {
"hlf a" => Op::Hlf,
"tpl a" => Op::Tpl,
"inc a" => Op::IncA,
"inc b" => Op::IncB,
_ => {
let index = i.wrapping_add(s.signed::<i32>() as usize);
match &s[..3] {
"jmp" => Op::Jmp(index),
"jie" => Op::Jie(index),
"jio" => Op::Jio(index),
_ => unreachable!(),
}
}
})
.collect()
}
pub fn part1(input: &[Op]) -> u64 {
execute(input, 0)
}
pub fn part2(input: &[Op]) -> u64 {
execute(input, 1)
}
fn execute(input: &[Op], mut a: u64) -> u64 {
let mut pc = 0;
let mut b = 0;
while pc < input.len() {
match input[pc] {
Op::Hlf => {
a /= 2;
pc += 1;
}
Op::Tpl => {
a *= 3;
pc += 1;
}
Op::IncA => {
a += 1;
pc += 1;
}
Op::IncB => {
b += 1;
pc += 1;
}
Op::Jmp(index) => pc = index,
Op::Jie(index) => pc = if a % 2 == 0 { index } else { pc + 1 },
Op::Jio(index) => pc = if a == 1 { index } else { pc + 1 },
}
}
b
}