|
1 | | -use rustc_hash::FxHashMap; |
2 | | - |
3 | | -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; |
| 1 | +use memoize::memoize; |
4 | 2 |
|
5 | 3 | advent_of_code::solution!(11); |
6 | 4 |
|
7 | 5 | type Stone = u64; |
8 | 6 | type Step = u8; |
9 | | -type MemoKey = (Step, Stone); |
10 | | -type Memo = FxHashMap<MemoKey, u64>; |
11 | 7 |
|
12 | | -fn blink_rec(memo: &mut Memo, stone: Stone, times: Step) -> u64 { |
| 8 | +#[memoize] |
| 9 | +fn blink_rec(stone: Stone, times: Step) -> u64 { |
13 | 10 | if times == 0 { |
14 | 11 | return 1; |
15 | 12 | } |
16 | | - if let Some(count) = memo.get(&(times, stone)) { |
17 | | - return *count; |
18 | | - } |
19 | 13 |
|
20 | | - let res = { |
21 | | - let mut digit_count = 0; |
22 | | - let mut temp = stone; |
23 | | - while temp > 0 { |
24 | | - digit_count += 1; |
25 | | - temp /= 10; |
26 | | - } |
| 14 | + if stone == 0 { |
| 15 | + return blink_rec(1, times - 1); |
| 16 | + } |
| 17 | + let mut digit_count = 0; |
| 18 | + let mut temp = stone; |
| 19 | + while temp > 0 { |
| 20 | + digit_count += 1; |
| 21 | + temp /= 10; |
| 22 | + } |
27 | 23 |
|
28 | | - if stone == 0 { |
29 | | - blink_rec(memo, 1, times - 1) |
30 | | - } else if digit_count % 2 == 0 { |
31 | | - let divisor = 10_u64.pow(digit_count / 2); |
32 | | - blink_rec(memo, stone / divisor, times - 1) |
33 | | - + blink_rec(memo, stone % divisor, times - 1) |
34 | | - } else { |
35 | | - blink_rec(memo, stone * 2024, times - 1) |
36 | | - } |
37 | | - }; |
| 24 | + if digit_count % 2 == 0 { |
| 25 | + let divisor = 10_u64.pow(digit_count / 2); |
| 26 | + return blink_rec(stone / divisor, times - 1) + blink_rec(stone % divisor, times - 1); |
| 27 | + } |
38 | 28 |
|
39 | | - memo.insert((times, stone), res); |
40 | | - res |
| 29 | + return blink_rec(stone * 2024, times - 1); |
41 | 30 | } |
42 | 31 |
|
43 | | -fn parse_input(input: &str) -> Vec<Stone> { |
44 | | - input |
| 32 | +fn solve(input: &str, times: Step) -> u64 { |
| 33 | + let stones = input |
45 | 34 | .split_whitespace() |
46 | 35 | .filter(|p| !p.is_empty()) |
47 | 36 | .map(|s| s.parse::<u64>()) |
48 | 37 | .filter(|r| r.is_ok()) |
49 | 38 | .map(|r| r.unwrap()) |
50 | | - .collect::<Vec<_>>() |
51 | | -} |
| 39 | + .collect::<Vec<_>>(); |
52 | 40 |
|
53 | | -pub fn part_one(input: &str) -> Option<u64> { |
54 | | - let times = 25; |
55 | | - let stones = parse_input(input); |
56 | | - let mut memo = FxHashMap::with_capacity_and_hasher(100000, Default::default()); |
57 | 41 | let count = stones |
58 | | - .iter() |
59 | | - .map(|start_stone| blink_rec(&mut memo, *start_stone, times)) |
| 42 | + .iter() // so fast that parallel is slower |
| 43 | + .map(|start_stone| blink_rec(*start_stone, times)) |
60 | 44 | .sum::<u64>(); |
61 | 45 |
|
62 | | - Some(count as u64) |
| 46 | + return count; |
63 | 47 | } |
64 | 48 |
|
65 | | -pub fn part_two(input: &str) -> Option<u64> { |
66 | | - let times = 75; |
67 | | - let stones = parse_input(input); |
68 | | - let count = stones |
69 | | - .par_iter() |
70 | | - .map(|start_stone| { |
71 | | - let mut memo = FxHashMap::with_capacity_and_hasher(100000, Default::default()); |
72 | | - blink_rec(&mut memo, *start_stone, times) |
73 | | - }) |
74 | | - .sum::<u64>(); |
| 49 | +pub fn part_one(input: &str) -> Option<u64> { |
| 50 | + Some(solve(input, 25)) |
| 51 | +} |
75 | 52 |
|
76 | | - Some(count as u64) |
| 53 | +pub fn part_two(input: &str) -> Option<u64> { |
| 54 | + Some(solve(input, 75)) |
77 | 55 | } |
78 | 56 |
|
79 | 57 | #[cfg(test)] |
|
0 commit comments