1use std::iter::{once, repeat_n};
7
8use crate::util::hash::*;
9use crate::util::parse::*;
10use crate::util::point::*;
11
12type Input<'a> = (Vec<(&'a str, usize)>, Combinations);
13type Combinations = FastMap<(char, char), Vec<String>>;
14type Cache = FastMap<(char, char, usize), usize>;
15
16pub fn parse(input: &str) -> Input<'_> {
19 let pairs = input.lines().zip(input.iter_unsigned()).collect();
20 (pairs, pad_combinations())
21}
22
23pub fn part1(input: &Input<'_>) -> usize {
24 chain(input, 3)
25}
26
27pub fn part2(input: &Input<'_>) -> usize {
28 chain(input, 26)
29}
30
31fn chain(input: &Input<'_>, depth: usize) -> usize {
32 let (pairs, combinations) = input;
33 let cache = &mut FastMap::with_capacity(500);
34 pairs.iter().map(|(code, numeric)| dfs(cache, combinations, code, depth) * numeric).sum()
35}
36
37fn dfs(cache: &mut Cache, combinations: &Combinations, code: &str, depth: usize) -> usize {
38 if depth == 0 {
40 return code.len();
41 }
42
43 let mut previous = 'A';
46 let mut result = 0;
47
48 for current in code.chars() {
49 let key = (previous, current, depth);
51
52 result += cache.get(&key).copied().unwrap_or_else(|| {
53 let presses = combinations[&(previous, current)]
56 .iter()
57 .map(|next| dfs(cache, combinations, next, depth - 1))
58 .min()
59 .unwrap();
60 cache.insert(key, presses);
61 presses
62 });
63
64 previous = current;
65 }
66
67 result
68}
69
70fn pad_combinations() -> Combinations {
74 let numeric_gap = Point::new(0, 3);
75 let numeric_keys = [
76 ('7', Point::new(0, 0)),
77 ('8', Point::new(1, 0)),
78 ('9', Point::new(2, 0)),
79 ('4', Point::new(0, 1)),
80 ('5', Point::new(1, 1)),
81 ('6', Point::new(2, 1)),
82 ('1', Point::new(0, 2)),
83 ('2', Point::new(1, 2)),
84 ('3', Point::new(2, 2)),
85 ('0', Point::new(1, 3)),
86 ('A', Point::new(2, 3)),
87 ];
88
89 let directional_gap = Point::new(0, 0);
90 let directional_keys = [
91 ('^', Point::new(1, 0)),
92 ('A', Point::new(2, 0)),
93 ('<', Point::new(0, 1)),
94 ('v', Point::new(1, 1)),
95 ('>', Point::new(2, 1)),
96 ];
97
98 let mut combinations = FastMap::with_capacity(145);
99 pad_routes(&mut combinations, &numeric_keys, numeric_gap);
100 pad_routes(&mut combinations, &directional_keys, directional_gap);
101 combinations
102}
103
104fn pad_routes(combinations: &mut Combinations, pad: &[(char, Point)], gap: Point) {
114 for &(first, from) in pad {
115 for &(second, to) in pad {
116 let horizontal = || move_sequence(from.x, to.x, '>', '<');
117 let vertical = || move_sequence(from.y, to.y, 'v', '^');
118 let routes = combinations.entry((first, second)).or_default();
119
120 if Point::new(from.x, to.y) != gap {
121 routes.push(vertical().chain(horizontal()).chain(once('A')).collect());
122 }
123
124 if from.x != to.x && from.y != to.y && Point::new(to.x, from.y) != gap {
125 routes.push(horizontal().chain(vertical()).chain(once('A')).collect());
126 }
127 }
128 }
129}
130
131fn move_sequence(from: i32, to: i32, positive: char, negative: char) -> impl Iterator<Item = char> {
132 let element = if from < to { positive } else { negative };
133 let count = from.abs_diff(to) as usize;
134 repeat_n(element, count)
135}