1use crate::util::iter::*;
30use crate::util::parse::*;
31
32const FIRST_HASH: [usize; 18] =
33 [43, 63, 78, 86, 92, 95, 98, 130, 294, 320, 332, 390, 401, 404, 475, 487, 554, 572];
34const SECOND_HASH: [usize; 33] = [
35 16, 31, 37, 38, 43, 44, 59, 67, 70, 76, 151, 170, 173, 174, 221, 286, 294, 312, 313, 376, 381,
36 401, 410, 447, 468, 476, 495, 498, 508, 515, 554, 580, 628,
37];
38
39#[derive(Clone, Copy)]
40pub struct Rule {
41 amount: u32,
42 next: usize,
43}
44
45type Bag = [Option<Rule>; 4];
46
47pub struct Haversack {
48 shiny_gold: usize,
49 bags: [Bag; 594],
50}
51
52pub fn parse(input: &str) -> Haversack {
53 let mut first_indices = [0; 676];
54 let mut second_indices = [0; 676];
55 let mut bags = [[None; 4]; 594];
56
57 FIRST_HASH.iter().enumerate().for_each(|(i, &h)| first_indices[h] = i);
58 SECOND_HASH.iter().enumerate().for_each(|(i, &h)| second_indices[h] = i);
59
60 let perfect_minimal_hash = |first: &str, second: &str| {
61 let first = first.as_bytes();
62 let a = first[0] as usize;
63 let b = first[1] as usize;
64
65 let second = second.as_bytes();
66 let c = second[0] as usize;
67 let d = (second[1] as usize) + (second.len() % 2);
68
69 first_indices[26 * a + b - 2619] + 18 * second_indices[26 * c + d - 2619]
70 };
71
72 for line in input.lines() {
73 let mut tokens = line.split_ascii_whitespace().chunk::<4>();
74 let [first, second, _, _] = tokens.next().unwrap();
75 let outer = perfect_minimal_hash(first, second);
76
77 for (slot, [amount, first, second, _]) in bags[outer].iter_mut().zip(tokens) {
78 let amount = amount.unsigned();
79 let next = perfect_minimal_hash(first, second);
80 *slot = Some(Rule { amount, next });
81 }
82 }
83
84 let shiny_gold = perfect_minimal_hash("shiny", "gold");
85 Haversack { shiny_gold, bags }
86}
87
88pub fn part1(input: &Haversack) -> usize {
89 fn helper(key: usize, haversack: &Haversack, cache: &mut [Option<bool>]) -> bool {
90 if let Some(value) = cache[key] {
91 return value;
92 }
93
94 let value =
95 haversack.bags[key].iter().flatten().any(|rule| helper(rule.next, haversack, cache));
96
97 cache[key] = Some(value);
98 value
99 }
100
101 let mut cache = vec![None; input.bags.len()];
102 cache[input.shiny_gold] = Some(true);
103 (0..input.bags.len()).filter(|&key| helper(key, input, &mut cache)).count() - 1
104}
105
106pub fn part2(input: &Haversack) -> u32 {
107 fn helper(key: usize, haversack: &Haversack, cache: &mut [Option<u32>]) -> u32 {
108 if let Some(value) = cache[key] {
109 return value;
110 }
111
112 let value = 1 + haversack.bags[key]
113 .iter()
114 .flatten()
115 .map(|rule| rule.amount * helper(rule.next, haversack, cache))
116 .sum::<u32>();
117
118 cache[key] = Some(value);
119 value
120 }
121
122 let mut cache = vec![None; input.bags.len()];
123 helper(input.shiny_gold, input, &mut cache) - 1
124}