Skip to content

Commit 91e4047

Browse files
committed
21th day
1 parent 5cacde3 commit 91e4047

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

data/examples/21.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
029A
2+
980A
3+
179A
4+
456A
5+
379A

src/bin/21.rs

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
advent_of_code::solution!(21);
2+
3+
use advent_of_code::maneatingape::heap::*;
4+
use advent_of_code::maneatingape::parse::*;
5+
use advent_of_code_macros::memoize;
6+
7+
fn parse_data(input: &str) -> Vec<u32> {
8+
input.lines().map(|line| line.unsigned()).collect()
9+
}
10+
11+
const ACTION_DIRECTIONAL: usize = 4;
12+
const ACTION_NUMERIC: usize = 10;
13+
14+
// [0]: LEFT, [1]: RIGHT, [2]: UP, [3]: DOWN
15+
const DIRECTIONS: [usize; 4] = [0, 1, 2, 3];
16+
17+
const NUMERIC_GRID: [[Option<usize>; 4]; 11] = {
18+
const A: usize = ACTION_NUMERIC;
19+
20+
[
21+
[None, Some(A), Some(2), None],
22+
[None, Some(2), Some(4), None],
23+
[Some(1), Some(3), Some(5), Some(0)],
24+
[Some(2), None, Some(6), Some(A)],
25+
[None, Some(5), Some(7), Some(1)],
26+
[Some(4), Some(6), Some(8), Some(2)],
27+
[Some(5), None, Some(9), Some(3)],
28+
[None, Some(8), None, Some(4)],
29+
[Some(7), Some(9), None, Some(5)],
30+
[Some(8), None, None, Some(6)],
31+
[Some(0), None, Some(3), None],
32+
]
33+
};
34+
35+
const DIRECTIONAL_GRID: [[Option<usize>; 4]; 5] = {
36+
const L: usize = 0;
37+
const R: usize = 1;
38+
const U: usize = 2;
39+
const D: usize = 3;
40+
const A: usize = ACTION_DIRECTIONAL;
41+
42+
[
43+
[None, Some(D), None, None],
44+
[Some(D), None, Some(A), None],
45+
[None, Some(A), None, Some(D)],
46+
[Some(L), Some(R), Some(U), None],
47+
[Some(U), None, None, Some(R)],
48+
]
49+
};
50+
51+
fn keypad_to_keypad<FNextPos, FNextStep>(
52+
start: usize,
53+
end: usize,
54+
next_pos_f: FNextPos,
55+
nexf_f: FNextStep,
56+
) -> u64
57+
where
58+
FNextPos: Fn(usize, usize) -> Option<usize>,
59+
FNextStep: Fn(usize, usize) -> u64,
60+
{
61+
let mut queue = MinHeap::new();
62+
queue.push(0, (start, ACTION_DIRECTIONAL, false));
63+
64+
while let Some((cost, (position, lower_position, pressed))) = queue.pop() {
65+
if position == end && !pressed {
66+
let move_cost = nexf_f(lower_position, ACTION_DIRECTIONAL);
67+
queue.push(cost + move_cost, (position, lower_position, true));
68+
continue;
69+
}
70+
71+
if position == end && pressed {
72+
return cost;
73+
}
74+
75+
for direction in DIRECTIONS {
76+
if let Some(new_position) = next_pos_f(position, direction) {
77+
let move_cost = nexf_f(lower_position, direction);
78+
queue.push(cost + move_cost, (new_position, direction, false));
79+
}
80+
}
81+
}
82+
83+
unreachable!()
84+
}
85+
86+
#[memoize]
87+
fn directional_to_numeric(start_numeric: usize, end_numeric: usize, n: usize) -> u64 {
88+
let next_pos_f = |pos: usize, d: usize| NUMERIC_GRID[pos][d];
89+
let next_f = |lower_pos: usize, d: usize| directional_to_directional(lower_pos, d, n - 1);
90+
91+
keypad_to_keypad(start_numeric, end_numeric, next_pos_f, next_f)
92+
}
93+
94+
#[memoize]
95+
fn directional_to_directional(start_directional: usize, end_directional: usize, n: usize) -> u64 {
96+
let next_pos_f = |pos: usize, d: usize| DIRECTIONAL_GRID[pos][d];
97+
let next_f: Box<dyn Fn(usize, usize) -> u64> = match n {
98+
0 => Box::new(|_: usize, _: usize| 1),
99+
_ => Box::new(|lower_pos: usize, d: usize| directional_to_directional(lower_pos, d, n - 1)),
100+
};
101+
102+
keypad_to_keypad(start_directional, end_directional, next_pos_f, next_f)
103+
}
104+
105+
fn part_x<const N: usize>(data: Vec<u32>) -> u64 {
106+
let flow = |code| {
107+
let d1 = code as usize / 100;
108+
let d2 = (code as usize / 10) % 10;
109+
let d3 = code as usize % 10;
110+
111+
[ACTION_NUMERIC, d1, d2, d3, ACTION_NUMERIC]
112+
};
113+
114+
let calculate_cost = |code| {
115+
flow(code)
116+
.windows(2)
117+
.map(|w| directional_to_numeric(w[0], w[1], N))
118+
.sum::<u64>()
119+
};
120+
121+
data.into_iter()
122+
.map(|code| code as u64 * calculate_cost(code))
123+
.sum::<u64>()
124+
}
125+
126+
pub fn part_one(input: &str) -> Option<u64> {
127+
let data = parse_data(input);
128+
129+
let result = part_x::<2>(data);
130+
131+
Some(result)
132+
}
133+
134+
pub fn part_two(input: &str) -> Option<u64> {
135+
let data = parse_data(input);
136+
137+
let result = part_x::<25>(data);
138+
139+
Some(result)
140+
}
141+
142+
#[cfg(test)]
143+
mod tests {
144+
use super::*;
145+
146+
#[test]
147+
fn test_part_one() {
148+
let input = advent_of_code::template::read_file("examples", DAY);
149+
let result = part_one(&input);
150+
assert_eq!(result, Some(126384));
151+
}
152+
153+
#[test]
154+
fn test_part_two() {
155+
let input = advent_of_code::template::read_file("examples", DAY);
156+
let result = part_two(&input);
157+
assert_eq!(result, Some(154115708116294));
158+
}
159+
}

0 commit comments

Comments
 (0)