Skip to content

Commit 80e11a5

Browse files
committed
Day 21 part 2 passing
1 parent 87549f4 commit 80e11a5

File tree

1 file changed

+219
-60
lines changed

1 file changed

+219
-60
lines changed

aoc-solver/src/y2024/day21.rs

Lines changed: 219 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::solution::{AocError, Solution};
99

1010
type Coords = (isize, isize);
1111
type Keyboard = HashMap<Coords, char>;
12+
type Sequence = Vec<char>;
1213

1314
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1415
enum Direction {
@@ -36,12 +37,12 @@ impl Direction {
3637
}
3738
}
3839

39-
fn parse(input: &str) -> Result<Vec<(Vec<char>, u32)>, AocError> {
40+
fn parse(input: &str) -> Result<Vec<(Sequence, u64)>, AocError> {
4041
let codes = input
4142
.lines()
4243
.map(|line| {
4344
let numeric_part = &line[0..3];
44-
let numeric: u32 = numeric_part
45+
let numeric: u64 = numeric_part
4546
.parse()
4647
.map_err(|err| AocError::parse(numeric_part, err))?;
4748
let code = line.chars().collect();
@@ -161,27 +162,7 @@ fn find_shortest(keys: &Keyboard, start: &Coords, end: &Coords) -> Vec<Vec<Direc
161162
shortest
162163
}
163164

164-
fn combine_parts(parts: &[Vec<Vec<char>>]) -> Vec<Vec<char>> {
165-
let mut combinations = vec![vec![]];
166-
167-
for group in parts {
168-
let mut new_combinations = Vec::new();
169-
170-
for combo_so_far in &combinations {
171-
for option in group {
172-
let mut combined = combo_so_far.clone();
173-
combined.extend_from_slice(option);
174-
new_combinations.push(combined);
175-
}
176-
}
177-
178-
combinations = new_combinations;
179-
}
180-
181-
combinations
182-
}
183-
184-
fn complexity(shortest: u32, numeric_part: u32) -> u32 {
165+
fn complexity(shortest: u64, numeric_part: u64) -> u64 {
185166
shortest * numeric_part
186167
}
187168

@@ -190,29 +171,85 @@ impl Robot {
190171
Self { keys, current }
191172
}
192173

193-
fn find_inputs_to_produce(&self, sequence: &[char]) -> Vec<Vec<char>> {
174+
fn find_inputs_to_produce(
175+
&self,
176+
sequence: &[char],
177+
cache: &mut HashMap<(char, char), Vec<Sequence>>,
178+
) -> Vec<Vec<Sequence>> {
194179
let mut parts = vec![];
195180
let mut current = self.current;
196181

197182
for button in sequence {
198-
let (target, _key) = self.keys.iter().find(|(_, key)| *key == button).unwrap();
199-
let shortest_paths = find_shortest(&self.keys, &current, target);
200-
201-
let part = shortest_paths
202-
.iter()
203-
.map(|shortest| convert_to_directional(shortest))
204-
.collect();
183+
let (target, target_key) = self.keys.iter().find(|(_, key)| *key == button).unwrap();
184+
let part = if let Some(cached) = cache.get(&(self.keys[&current], *target_key)) {
185+
cached.clone()
186+
} else {
187+
let shortest_paths = find_shortest(&self.keys, &current, target);
188+
189+
shortest_paths
190+
.iter()
191+
.map(|shortest| convert_to_directional(shortest))
192+
.collect()
193+
};
205194

206195
parts.push(part);
207196

208197
current = *target;
209198
}
210199

211-
combine_parts(&parts)
200+
parts
212201
}
213202
}
214203

215-
fn convert_to_directional(shortest: &[Direction]) -> Vec<char> {
204+
fn recursive(
205+
keys: &HashMap<Coords, char>,
206+
sequence: &[char],
207+
remaining_robots: u32,
208+
cache: &mut HashMap<(String, u32), u64>,
209+
shortest_cache: &mut HashMap<(char, char), Vec<Sequence>>,
210+
) -> u64 {
211+
let sequence_key: String = sequence.iter().collect();
212+
if let Some(cached) = cache.get(&(sequence_key.clone(), remaining_robots)) {
213+
return *cached;
214+
}
215+
216+
let mut current = (2, 0);
217+
let mut count = 0;
218+
219+
for button in sequence {
220+
let (target, target_key) = keys.iter().find(|(_, key)| *key == button).unwrap();
221+
let possible = if let Some(cached) = shortest_cache.get(&(keys[&current], *target_key)) {
222+
cached.clone()
223+
} else {
224+
let shortest_paths = find_shortest(keys, &current, target);
225+
226+
shortest_paths
227+
.iter()
228+
.map(|shortest| convert_to_directional(shortest))
229+
.collect()
230+
};
231+
232+
count += possible
233+
.iter()
234+
.map(|part| {
235+
if remaining_robots > 1 {
236+
recursive(keys, part, remaining_robots - 1, cache, shortest_cache)
237+
} else {
238+
part.len() as u64
239+
}
240+
})
241+
.min()
242+
.unwrap();
243+
244+
current = *target;
245+
}
246+
247+
cache.insert((sequence_key, remaining_robots), count);
248+
249+
count
250+
}
251+
252+
fn convert_to_directional(shortest: &[Direction]) -> Sequence {
216253
let mut input = vec![];
217254
for direction in shortest {
218255
input.push(match direction {
@@ -226,51 +263,73 @@ fn convert_to_directional(shortest: &[Direction]) -> Vec<char> {
226263
input
227264
}
228265

229-
fn find_shortest_inputs(sequence: &[char]) -> Vec<char> {
266+
fn find_shortest_inputs(
267+
sequence: &[char],
268+
robots: u32,
269+
cache: &mut HashMap<(String, u32), u64>,
270+
shortest_cache: &mut HashMap<(char, char), Vec<Sequence>>,
271+
) -> u64 {
230272
let (numpad, numpad_start) = create_numerical_keypad();
231-
let (keypad, keypad_start) = create_directional_keypad();
273+
let (keypad, _) = create_directional_keypad();
232274

233275
let numpad_robot = Robot::new(numpad, numpad_start);
234-
let keypad_robot_1 = Robot::new(keypad.clone(), keypad_start);
235-
let keypad_robot_2 = Robot::new(keypad, keypad_start);
276+
let numpad_inputs = numpad_robot.find_inputs_to_produce(sequence, shortest_cache);
236277

237-
let numpad_inputs = numpad_robot.find_inputs_to_produce(sequence);
238-
239-
let shortest: Vec<_> = numpad_inputs
278+
let shortest = numpad_inputs
240279
.iter()
241-
.flat_map(|numpad_input| keypad_robot_1.find_inputs_to_produce(numpad_input))
242-
.flat_map(|keypad_input| keypad_robot_2.find_inputs_to_produce(&keypad_input))
243-
.min_by(|a, b| a.len().cmp(&b.len()))
244-
.unwrap_or(Vec::new());
280+
.map(|inputs| {
281+
inputs
282+
.iter()
283+
.map(|input| recursive(&keypad, input, robots, cache, shortest_cache))
284+
.min()
285+
.unwrap()
286+
})
287+
.sum();
245288

246289
shortest
247290
}
248291

249292
pub struct Day21;
250293
impl Solution for Day21 {
251-
type A = u32;
252-
type B = u32;
294+
type A = u64;
295+
type B = u64;
253296

254297
fn default_input(&self) -> &'static str {
255298
include_str!("../../../inputs/2024/day21.txt")
256299
}
257300

258-
fn part_1(&self, input: &str) -> Result<u32, AocError> {
301+
fn part_1(&self, input: &str) -> Result<u64, AocError> {
259302
let codes = parse(input)?;
260303

304+
let mut cache = HashMap::new();
305+
let mut shortest_cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
306+
261307
let complexities = codes
262308
.into_iter()
263309
.map(|(code, numeric_part)| {
264-
let shortest = find_shortest_inputs(&code).len() as u32;
310+
let shortest = find_shortest_inputs(&code, 2, &mut cache, &mut shortest_cache);
265311
complexity(shortest, numeric_part)
266312
})
267313
.sum();
268314

269315
Ok(complexities)
270316
}
271317

272-
fn part_2(&self, input: &str) -> Result<u32, AocError> {
273-
unimplemented!()
318+
fn part_2(&self, input: &str) -> Result<u64, AocError> {
319+
let codes = parse(input)?;
320+
321+
let mut cache = HashMap::new();
322+
let mut shortest_cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
323+
324+
let complexities = codes
325+
.into_iter()
326+
.map(|(code, numeric_part)| {
327+
let shortest = find_shortest_inputs(&code, 25, &mut cache, &mut shortest_cache);
328+
complexity(shortest, numeric_part)
329+
})
330+
.sum();
331+
332+
Ok(complexities)
274333
}
275334
}
276335

@@ -291,44 +350,144 @@ mod tests {
291350
assert_eq!(Day21.part_1(INPUT), Ok(126384));
292351
}
293352

353+
#[test]
354+
fn it_solves_part2_real() {
355+
assert_eq!(Day21.part_2(Day21.default_input()), Ok(167538833832712));
356+
}
357+
294358
#[test]
295359
fn it_finds_shortest_numpad() {
296-
let (keypad, a) = create_numerical_keypad();
360+
let (numpad, a) = create_numerical_keypad();
361+
let robot = Robot::new(numpad, a);
362+
let mut cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
363+
364+
assert_eq!(
365+
robot.find_inputs_to_produce(&['0', '2', '9', 'A'], &mut cache),
366+
vec![
367+
vec![vec!['<', 'A']], // A - 0
368+
vec![vec!['^', 'A']], // 0 - 2
369+
vec![
370+
// 2 - 9
371+
vec!['^', '>', '^', 'A'],
372+
vec!['^', '^', '>', 'A'],
373+
vec!['>', '^', '^', 'A']
374+
],
375+
vec![vec!['v', 'v', 'v', 'A']] // 9 - A
376+
]
377+
);
378+
}
297379

380+
#[test]
381+
fn it_finds_shortest_keypad() {
382+
let (keypad, a) = create_directional_keypad();
383+
let robot = Robot::new(keypad, a);
384+
let mut cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
385+
386+
assert_eq!(
387+
robot.find_inputs_to_produce(&['<', 'A'], &mut cache),
388+
vec![
389+
vec![vec!['v', '<', '<', 'A'], vec!['<', 'v', '<', 'A']], // A - <
390+
vec![vec!['>', '^', '>', 'A'], vec!['>', '>', '^', 'A']] // < - A
391+
]
392+
);
393+
}
394+
395+
#[test]
396+
fn it_finds_shortest_keypad_2() {
397+
let (keypad, a) = create_directional_keypad();
298398
let robot = Robot::new(keypad, a);
399+
let mut cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
299400

300401
assert_eq!(
301-
robot.find_inputs_to_produce(&['0', '2', '9', 'A']),
402+
robot.find_inputs_to_produce(&['^', '>', '^', 'A'], &mut cache),
302403
vec![
303-
"<A^A^>^AvvvA".chars().collect::<Vec<_>>(),
304-
"<A^A^^>AvvvA".chars().collect::<Vec<_>>(),
305-
"<A^A>^^AvvvA".chars().collect::<Vec<_>>(),
404+
vec![vec!['<', 'A']], // A - ^
405+
vec![vec!['>', 'v', 'A'], vec!['v', '>', 'A']], // ^ - >
406+
vec![vec!['^', '<', 'A'], vec!['<', '^', 'A']], // > - ^
407+
vec![vec!['>', 'A']] // ^ - A
408+
]
409+
);
410+
}
411+
412+
#[test]
413+
fn it_finds_shortest_keypad_3() {
414+
let (keypad, a) = create_directional_keypad();
415+
let robot = Robot::new(keypad, a);
416+
let mut cache: HashMap<(char, char), Vec<Sequence>> = HashMap::new();
417+
418+
assert_eq!(
419+
robot.find_inputs_to_produce(&['^', '^', '>', 'A'], &mut cache),
420+
vec![
421+
vec![vec!['<', 'A']], // A - ^
422+
vec![vec!['A']], // ^ - ^
423+
vec![vec!['>', 'v', 'A'], vec!['v', '>', 'A']], // ^ - >
424+
vec![vec!['^', 'A']] // > - A
306425
]
307426
);
308427
}
309428

310429
#[test]
311430
fn it_finds_min_inputs_length_1() {
312-
assert_eq!(find_shortest_inputs(&['0', '2', '9', 'A']).len(), 68);
431+
assert_eq!(
432+
find_shortest_inputs(
433+
&['0', '2', '9', 'A'],
434+
2,
435+
&mut HashMap::new(),
436+
&mut HashMap::new()
437+
),
438+
68
439+
);
313440
}
314441

315442
#[test]
316443
fn it_finds_min_inputs_length_2() {
317-
assert_eq!(find_shortest_inputs(&['9', '8', '0', 'A']).len(), 60);
444+
assert_eq!(
445+
find_shortest_inputs(
446+
&['9', '8', '0', 'A'],
447+
2,
448+
&mut HashMap::new(),
449+
&mut HashMap::new()
450+
),
451+
60
452+
);
318453
}
319454

320455
#[test]
321456
fn it_finds_min_inputs_length_3() {
322-
assert_eq!(find_shortest_inputs(&['1', '7', '9', 'A']).len(), 68);
457+
assert_eq!(
458+
find_shortest_inputs(
459+
&['1', '7', '9', 'A'],
460+
2,
461+
&mut HashMap::new(),
462+
&mut HashMap::new()
463+
),
464+
68
465+
);
323466
}
324467

325468
#[test]
326469
fn it_finds_min_inputs_length_4() {
327-
assert_eq!(find_shortest_inputs(&['4', '5', '6', 'A']).len(), 64);
470+
assert_eq!(
471+
find_shortest_inputs(
472+
&['4', '5', '6', 'A'],
473+
2,
474+
&mut HashMap::new(),
475+
&mut HashMap::new()
476+
),
477+
64
478+
);
328479
}
329480

330481
#[test]
331482
fn it_finds_min_inputs_length_5() {
332-
assert_eq!(find_shortest_inputs(&['3', '7', '9', 'A']).len(), 64);
483+
assert_eq!(
484+
find_shortest_inputs(
485+
&['3', '7', '9', 'A'],
486+
2,
487+
&mut HashMap::new(),
488+
&mut HashMap::new()
489+
),
490+
64
491+
);
333492
}
334493
}

0 commit comments

Comments
 (0)