1use crate::util::iter::*;
15use crate::util::parse::*;
16
17pub enum Instruction {
18 Acc(i16),
19 Jmp(i16),
20 Nop(i16),
21}
22
23impl Instruction {
24 fn from([a, b]: [&str; 2]) -> Instruction {
25 let amount = b.signed();
26 match a {
27 "acc" => Instruction::Acc(amount),
28 "jmp" => Instruction::Jmp(amount),
29 "nop" => Instruction::Nop(amount),
30 _ => unreachable!(),
31 }
32 }
33}
34
35enum State {
36 Infinite(i32),
37 Halted(i32),
38}
39
40pub fn parse(input: &str) -> Vec<Instruction> {
41 input.split_ascii_whitespace().chunk::<2>().map(Instruction::from).collect()
42}
43
44pub fn part1(input: &[Instruction]) -> i32 {
45 let mut visited = vec![false; input.len()];
46
47 match execute(input, 0, 0, &mut visited) {
48 State::Infinite(acc) => acc,
49 State::Halted(_) => unreachable!(),
50 }
51}
52
53pub fn part2(input: &[Instruction]) -> i32 {
54 let mut pc = 0;
55 let mut acc = 0;
56 let visited = &mut vec![false; input.len()];
57
58 loop {
59 match input[pc] {
60 Instruction::Acc(arg) => {
61 pc += 1;
62 acc += arg as i32;
63 }
64 Instruction::Jmp(arg) => {
65 let speculative = pc + 1;
66 match execute(input, speculative, acc, visited) {
67 State::Infinite(_) => pc = pc.wrapping_add(arg as usize),
68 State::Halted(acc) => break acc,
69 }
70 }
71 Instruction::Nop(arg) => {
72 let speculative = pc.wrapping_add(arg as usize);
73 match execute(input, speculative, acc, visited) {
74 State::Infinite(_) => pc += 1,
75 State::Halted(acc) => break acc,
76 }
77 }
78 }
79 }
80}
81
82fn execute(input: &[Instruction], mut pc: usize, mut acc: i32, visited: &mut [bool]) -> State {
83 loop {
84 if pc >= input.len() {
85 break State::Halted(acc);
86 } else if visited[pc] {
87 break State::Infinite(acc);
88 }
89
90 visited[pc] = true;
91
92 match input[pc] {
93 Instruction::Acc(arg) => {
94 pc += 1;
95 acc += arg as i32;
96 }
97 Instruction::Jmp(arg) => {
98 pc = pc.wrapping_add(arg as usize);
99 }
100 Instruction::Nop(_) => {
101 pc += 1;
102 }
103 }
104 }
105}