Skip to main content

advent_of_code/year2017/
day25.rs

1use crate::input::Input;
2
3struct Action {
4    write_one: bool,
5    move_direction: i8,
6    next_state: u8,
7}
8
9struct State {
10    if_zero_action: Action,
11    if_one_action: Action,
12}
13
14struct Tape {
15    slots: Vec<bool>,
16}
17
18impl Tape {
19    const MIN_POSITION: i32 = -10000;
20    const MAX_POSITION: i32 = 10000;
21
22    fn new() -> Self {
23        Self {
24            slots: vec![false; (Self::MAX_POSITION - Self::MIN_POSITION) as usize],
25        }
26    }
27
28    const fn position_to_idx(position: i32) -> usize {
29        (position - Self::MIN_POSITION) as usize
30    }
31
32    fn is_one_at(&self, position: i32) -> bool {
33        let idx = Self::position_to_idx(position);
34        self.slots[idx]
35    }
36
37    fn set_slot(&mut self, position: i32, is_one: bool) {
38        let idx = Self::position_to_idx(position);
39        self.slots[idx] = is_one;
40    }
41
42    fn diagnostic_checksum(&self) -> usize {
43        self.slots.iter().filter(|&&b| b).count()
44    }
45}
46
47pub fn solve(input: &Input) -> Result<usize, String> {
48    let mut tape = Tape::new();
49    let mut target_steps = 0;
50
51    let mut states: Vec<State> = Vec::new();
52    let on_error = || "Invalid input".to_string();
53
54    for (count, text) in input.text.split("\n\n").enumerate() {
55        if count == 0 {
56            target_steps = text
57                .split(' ')
58                .nth(8)
59                .ok_or_else(on_error)?
60                .parse::<u32>()
61                .map_err(|_| on_error())?;
62        } else {
63            let words: Vec<&str> = text.split(' ').collect();
64
65            if words.len() < 69 {
66                return Err(on_error());
67            }
68
69            let if_zero_action = Action {
70                write_one: words[17] == "1.\n",
71                move_direction: if words[27] == "right.\n" { 1 } else { -1 },
72                next_state: words[35]
73                    .bytes()
74                    .next()
75                    .ok_or_else(on_error)?
76                    .checked_sub(b'A')
77                    .ok_or_else(on_error)?,
78            };
79            let if_one_action = Action {
80                write_one: words[50] == "1.\n",
81                move_direction: if words[60] == "right.\n" { 1 } else { -1 },
82                next_state: words[68]
83                    .bytes()
84                    .next()
85                    .ok_or_else(on_error)?
86                    .checked_sub(b'A')
87                    .ok_or_else(on_error)?,
88            };
89            states.push(State {
90                if_zero_action,
91                if_one_action,
92            });
93        }
94    }
95
96    if states.is_empty() {
97        return Err(on_error());
98    }
99
100    let mut current_state = 0;
101    let mut current_position = 0_i32;
102
103    if states.iter().any(|s| {
104        usize::from(s.if_one_action.next_state.max(s.if_zero_action.next_state)) >= states.len()
105    }) {
106        return Err("Invalid input - reference to non-defined state".to_string());
107    }
108
109    for _ in 0..target_steps {
110        let current_action = if tape.is_one_at(current_position) {
111            &states[current_state].if_one_action
112        } else {
113            &states[current_state].if_zero_action
114        };
115
116        tape.set_slot(current_position, current_action.write_one);
117
118        current_position += i32::from(current_action.move_direction);
119        if !(Tape::MIN_POSITION..Tape::MAX_POSITION).contains(&current_position) {
120            return Err(format!(
121                "Too long tape {} - only allowed inside [{},{}]",
122                current_position,
123                Tape::MIN_POSITION,
124                Tape::MAX_POSITION
125            ));
126        }
127        current_state = current_action.next_state as usize;
128    }
129
130    Ok(tape.diagnostic_checksum())
131}
132
133#[test]
134pub fn tests() {
135    let example = "Begin in state A.
136Perform a diagnostic checksum after 6 steps.
137
138In state A:
139  If the current value is 0:
140    - Write the value 1.
141    - Move one slot to the right.
142    - Continue with state B.
143  If the current value is 1:
144    - Write the value 0.
145    - Move one slot to the left.
146    - Continue with state B.
147
148In state B:
149  If the current value is 0:
150    - Write the value 1.
151    - Move one slot to the left.
152    - Continue with state A.
153  If the current value is 1:
154    - Write the value 1.
155    - Move one slot to the right.
156    - Continue with state A.";
157    test_part_one!(example => 3);
158
159    let real_input = include_str!("day25_input.txt");
160    test_part_one!(real_input => 633);
161}