advent_of_code/year2018/
day21.rs

1use super::elfcode::Program;
2use crate::input::Input;
3use std::collections::HashSet;
4
5fn parse(input_string: &str) -> Result<Program, String> {
6    let program = Program::parse(input_string)?;
7    if program.instructions.len() != 31 {
8        return Err("Expected 31 instructions in program".to_string());
9    }
10    Ok(program)
11}
12
13const MAX_INSTRUCTIONS: u64 = 1_000_000;
14
15pub fn solve(input: &Input) -> Result<u64, String> {
16    let mut program = parse(input.text)?;
17    if input.is_part_one() {
18        // The last three instructions are (as seen with program.pretty_print()):
19        //
20        // 28: r4 = (r3 == r0) ? 1 : 0
21        // 29: goto r4 + 30
22        // 30: goto 6
23        //
24        // which exits on instruction 29 only if r4 is non-zero, which means r0 must equal r3.
25        //
26        // Since this is the only place in the program where register 0 is referenced, we can
27        // set register 0 to the value it's first compared with here to exit as soon as possible.
28
29        let mut loop_count = 0;
30        while program.instruction_pointer()? != 29 {
31            program.execute_one_instruction()?;
32
33            loop_count += 1;
34            if loop_count > MAX_INSTRUCTIONS {
35                return Err(format!("Aborted after {loop_count} instructions"));
36            }
37        }
38        Ok(program.registers.values[program.instructions[28].a as usize])
39    } else {
40        let mut seen = HashSet::new();
41        let mut last_value = 0;
42        let mut loop_count = 0;
43        loop {
44            let ip = program.instruction_pointer()?;
45            if ip == 14 {
46                if program.registers.values[program.instructions[13].c as usize] == 0 {
47                    program.registers.values[program.instructions[6].c as usize] /= 256;
48                    program.registers.values[program.instruction_pointer_index as usize] = 8;
49                }
50            } else if ip == 29 {
51                let value = program.registers.values[program.instructions[28].a as usize];
52                if seen.insert(value) {
53                    last_value = value;
54                } else {
55                    return Ok(last_value);
56                }
57            }
58            program.execute_one_instruction()?;
59
60            loop_count += 1;
61            if loop_count > MAX_INSTRUCTIONS {
62                return Err(format!("Aborted after {loop_count} instructions"));
63            }
64        }
65    }
66}
67
68#[test]
69fn tests() {
70    use crate::input::{test_part_one, test_part_two};
71    let input = include_str!("day21_input.txt");
72    test_part_one!(input => 7_216_956);
73    test_part_two!(input => 14_596_916);
74}