Skip to main content

year2019/
day21.rs

1use crate::intcode::Interpreter;
2use crate::intcode::features::Day09Features;
3use utils::prelude::*;
4
5/// Interpreting machine code to evaluate a boolean script.
6#[derive(Clone, Debug)]
7pub struct Day21 {
8    interpreter: Interpreter,
9}
10
11// De Morgan's laws allow writing `J = (!A || !B || !C) && D` as `J = !(A && B && C) && D`, which
12// can be written in 5 boolean instructions without using T.
13const WALK_SCRIPT: &[u8] = b"OR A J\n\
14AND B J\n\
15AND C J\n\
16NOT J J\n\
17AND D J\n\
18WALK\n";
19
20// Jumping avoids holes at A, B and C before landing at D. After landing, H is another 4 tiles
21// ahead, so if there is ground at H, a jump to D can be followed by an immediate second jump to H.
22// Jump now if there is ground at D, and a hole at A or B, or a hole at C as well as ground at H.
23// Otherwise walk once so C becomes the new B and E becomes the new landing tile D. This gives
24// `J = (!A || !B || (!C && H)) && D`, which can be rewritten as `J = !(A && B && (C || !H)) && D`,
25// using 6 boolean instructions without using T.
26const RUN_SCRIPT: &[u8] = b"NOT H J\n\
27OR C J\n\
28AND B J\n\
29AND A J\n\
30NOT J J\n\
31AND D J\n\
32RUN\n";
33
34impl Day21 {
35    pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
36        Ok(Self {
37            interpreter: Interpreter::parse(input, 1)?,
38        })
39    }
40
41    #[must_use]
42    pub fn part1(&self) -> i64 {
43        self.run(WALK_SCRIPT)
44    }
45
46    #[must_use]
47    pub fn part2(&self) -> i64 {
48        self.run(RUN_SCRIPT)
49    }
50
51    fn run(&self, script: &[u8]) -> i64 {
52        let mut interpreter = self.interpreter.clone();
53        interpreter.push_bytes(script);
54
55        loop {
56            if let x @ 128.. = interpreter.expect_output::<Day09Features>() {
57                return x;
58            }
59        }
60    }
61}
62
63examples!(Day21 -> (i64, i64) []);