1use std::collections::VecDeque;
2use utils::prelude::*;
3
4#[derive(Clone, Debug)]
6pub struct Day25 {
7 start: State,
8 steps: u32,
9 rules: Vec<[Rule; 2]>,
10}
11
12#[derive(Copy, Clone, Debug)]
13struct Rule {
14 write_value: bool,
15 move_dir: Direction,
16 next_state: State,
17}
18
19parser::parsable_enum! {
20 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
21 enum Direction {
22 "left" => Left = -1,
23 "right" => Right = 1,
24 }
25}
26
27parser::parsable_enum! {
28 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
29 enum State {
30 "A" => A,
31 "B" => B,
32 "C" => C,
33 "D" => D,
34 "E" => E,
35 "F" => F,
36 }
37}
38
39impl Day25 {
40 pub fn new(input: &str, _: InputType) -> Result<Self, InputError> {
41 let rule = parser::byte_range(b'0'..=b'1')
42 .map(|b| b == b'1')
43 .with_prefix(parser::eol().then(" - Write the value "))
44 .with_suffix(".".then(parser::eol()))
45 .then(
46 Direction::PARSER
47 .with_prefix(" - Move one slot to the ")
48 .with_suffix(".".then(parser::eol())),
49 )
50 .then(
51 State::PARSER
52 .with_consumed()
53 .with_prefix(" - Continue with state ")
54 .with_suffix(".".then(parser::eol())),
55 )
56 .map(|(write_value, move_dir, (next_state, state_pos))| {
57 (
58 Rule {
59 write_value,
60 move_dir,
61 next_state,
62 },
63 state_pos,
64 )
65 });
66
67 let ((start, start_pos), steps, rules) = State::PARSER
68 .with_consumed()
69 .with_prefix("Begin in state ")
70 .with_suffix(".".then(parser::eol()))
71 .then(
72 parser::u32()
73 .with_prefix("Perform a diagnostic checksum after ")
74 .with_suffix(" steps.".then(parser::eol()).then(parser::eol())),
75 )
76 .then(
77 State::PARSER
78 .with_consumed()
79 .with_prefix("In state ")
80 .with_suffix(":".then(parser::eol()))
81 .then(rule.with_prefix(" If the current value is 0:"))
82 .then(rule.with_prefix(" If the current value is 1:"))
83 .repeat(parser::eol(), 2),
84 )
85 .parse_complete(input)?;
86
87 if let Some((_, &((_, pos), _, _))) = rules
88 .iter()
89 .enumerate()
90 .find(|&(i, &((s, _), _, _))| s as usize != i)
91 {
92 return Err(InputError::new(input, pos, "rules are not in order"));
93 }
94
95 if start as usize >= rules.len() {
96 return Err(InputError::new(input, start_pos, "invalid start state"));
97 }
98
99 if let Some(pos) = rules
100 .iter()
101 .map(|(_, r0, _)| r0)
102 .chain(rules.iter().map(|(_, _, r1)| r1))
103 .find_map(|&(r, pos)| (r.next_state as usize >= rules.len()).then_some(pos))
104 {
105 return Err(InputError::new(input, pos, "invalid next state"));
106 }
107
108 Ok(Self {
109 start,
110 steps,
111 rules: rules
112 .into_iter()
113 .map(|(_, (r0, _), (r1, _))| [r0, r1])
114 .collect(),
115 })
116 }
117
118 #[must_use]
119 pub fn part1(&self) -> u32 {
120 type Element = u32;
123
124 let mut tape: VecDeque<Element> = VecDeque::with_capacity(512);
125 tape.push_back(0);
126 let mut element_index = 0;
127 let mut bit_index = 0;
128 let mut state = self.start;
129
130 #[derive(Debug)]
131 struct StateTransition {
132 from_state: State,
133 from_element: Element,
134 to_state: State,
135 to_element: Element,
136 steps: u32,
137 element_index: usize,
138 }
139 let mut cache: [Vec<StateTransition>; 12] = Default::default();
141
142 let mut step = 0;
143 'outer: while step < self.steps {
144 if element_index == 0 {
146 tape.push_front(0);
147 element_index += 1;
148 } else if element_index == tape.len() {
149 tape.push_back(0);
150 }
151
152 for t in &cache[(state as usize * 2) | (bit_index > 0) as usize] {
155 if t.from_element == tape[element_index] && t.steps + step <= self.steps {
156 while element_index > 0
157 && element_index < tape.len()
158 && t.from_state == state
159 && t.from_element == tape[element_index]
160 && t.steps + step <= self.steps
161 {
162 tape[element_index] = t.to_element;
163 element_index = element_index.wrapping_add(t.element_index);
164 state = t.to_state;
165 step += t.steps;
166 }
167
168 continue 'outer;
169 }
170 }
171
172 let starting_element_index = element_index;
173 let starting_bit_index = bit_index;
174 let starting_element = tape[element_index];
175 let starting_step = step;
176 let starting_state = state;
177
178 let mut element = starting_element;
179 while step < self.steps && bit_index < Element::BITS {
180 let rule = &self.rules[state as usize][((element >> bit_index) & 1) as usize];
181
182 element =
183 (element & !(1 << bit_index)) | (Element::from(rule.write_value) << bit_index);
184 bit_index = bit_index.wrapping_add_signed(rule.move_dir as i32);
185 state = rule.next_state;
186 step += 1;
187 }
188 tape[element_index] = element;
189
190 if step == self.steps {
192 break;
193 } else if bit_index == Element::BITS {
194 bit_index = 0;
195 element_index += 1;
196 } else {
197 bit_index = Element::BITS - 1;
198 element_index -= 1;
199 }
200
201 if starting_bit_index == bit_index {
203 cache[((starting_state as usize) << 1) | (bit_index > 0) as usize].push(
204 StateTransition {
205 from_state: starting_state,
206 from_element: starting_element,
207 to_state: state,
208 to_element: element,
209 steps: step - starting_step,
210 element_index: element_index.wrapping_sub(starting_element_index),
211 },
212 );
213 }
214 }
215
216 tape.iter().map(|&v| v.count_ones()).sum()
217 }
218
219 #[must_use]
220 pub fn part2(&self) -> &'static str {
221 "🎄"
222 }
223}
224
225examples!(Day25 -> (u32, &'static str) [
226 {file: "day25_example0.txt", part1: 3},
227]);