Skip to main content

aoc/year2015/
day23.rs

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