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(".".with_eol())
45 .then(
46 Direction::PARSER
47 .with_prefix(" - Move one slot to the ")
48 .with_suffix(".".with_eol()),
49 )
50 .then(
51 State::PARSER
52 .with_consumed()
53 .with_prefix(" - Continue with state ")
54 .with_suffix(".".with_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(".".with_eol())
71 .then(
72 parser::u32()
73 .with_prefix("Perform a diagnostic checksum after ")
74 .with_suffix(" steps.".with_eol().with_eol()),
75 )
76 .then(
77 State::PARSER
78 .with_consumed()
79 .with_prefix("In state ")
80 .with_suffix(":".with_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 #[derive(Debug)]
125 struct StateTransition {
126 from_state: State,
127 from_element: Element,
128 to_state: State,
129 to_element: Element,
130 steps: u32,
131 element_index: usize,
132 }
133
134 let mut tape: VecDeque<Element> = VecDeque::with_capacity(512);
135 tape.push_back(0);
136 let mut element_index = 0;
137 let mut bit_index = 0;
138 let mut state = self.start;
139
140 let mut cache: [Vec<StateTransition>; 12] = Default::default();
142
143 let mut step = 0;
144 'outer: while step < self.steps {
145 if element_index == 0 {
147 tape.push_front(0);
148 element_index += 1;
149 } else if element_index == tape.len() {
150 tape.push_back(0);
151 }
152
153 for t in &cache[(state as usize * 2) | usize::from(bit_index > 0)] {
156 if t.from_element == tape[element_index] && t.steps + step <= self.steps {
157 while element_index > 0
158 && element_index < tape.len()
159 && t.from_state == state
160 && t.from_element == tape[element_index]
161 && t.steps + step <= self.steps
162 {
163 tape[element_index] = t.to_element;
164 element_index = element_index.wrapping_add(t.element_index);
165 state = t.to_state;
166 step += t.steps;
167 }
168
169 continue 'outer;
170 }
171 }
172
173 let starting_element_index = element_index;
174 let starting_bit_index = bit_index;
175 let starting_element = tape[element_index];
176 let starting_step = step;
177 let starting_state = state;
178
179 let mut element = starting_element;
180 while step < self.steps && bit_index < Element::BITS {
181 let rule = &self.rules[state as usize][((element >> bit_index) & 1) as usize];
182
183 element =
184 (element & !(1 << bit_index)) | (Element::from(rule.write_value) << bit_index);
185 bit_index = bit_index.wrapping_add_signed(rule.move_dir as i32);
186 state = rule.next_state;
187 step += 1;
188 }
189 tape[element_index] = element;
190
191 if step == self.steps {
193 break;
194 } else if bit_index == Element::BITS {
195 bit_index = 0;
196 element_index += 1;
197 } else {
198 bit_index = Element::BITS - 1;
199 element_index -= 1;
200 }
201
202 if starting_bit_index == bit_index {
204 cache[((starting_state as usize) << 1) | usize::from(bit_index > 0)].push(
205 StateTransition {
206 from_state: starting_state,
207 from_element: starting_element,
208 to_state: state,
209 to_element: element,
210 steps: step - starting_step,
211 element_index: element_index.wrapping_sub(starting_element_index),
212 },
213 );
214 }
215 }
216
217 tape.iter().map(|&v| v.count_ones()).sum()
218 }
219
220 #[must_use]
221 pub fn part2(&self) -> &'static str {
222 "🎄"
223 }
224}
225
226examples!(Day25 -> (u32, &'static str) [
227 {file: "day25_example0.txt", part1: 3},
228]);