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