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 (index, chunk) in tokens.enumerate() {
78 let [amount, first, second, _] = chunk;
79 let amount = amount.unsigned();
80 let next = perfect_minimal_hash(first, second);
81 bags[outer][index] = Some(Rule { amount, next });
82 }
83 }
84
85 let shiny_gold = perfect_minimal_hash("shiny", "gold");
86 Haversack { shiny_gold, bags }
87}
88
89pub fn part1(input: &Haversack) -> usize {
90 fn helper(key: usize, haversack: &Haversack, cache: &mut [Option<bool>]) -> bool {
91 if let Some(value) = cache[key] {
92 value
93 } else {
94 let mut value = false;
95 let mut iter = haversack.bags[key].iter();
96
97 while let Some(Some(Rule { next, .. })) = iter.next() {
98 if helper(*next, haversack, cache) {
99 value = true;
100 break;
101 }
102 }
103
104 cache[key] = Some(value);
105 value
106 }
107 }
108
109 let mut cache = vec![None; input.bags.len()];
110 cache[input.shiny_gold] = Some(true);
111 (0..input.bags.len()).filter(|&key| helper(key, input, &mut cache)).count() - 1
112}
113
114pub fn part2(input: &Haversack) -> u32 {
115 fn helper(key: usize, haversack: &Haversack, cache: &mut [Option<u32>]) -> u32 {
116 if let Some(value) = cache[key] {
117 value
118 } else {
119 let mut value = 1;
120 let mut iter = haversack.bags[key].iter();
121
122 while let Some(Some(Rule { amount, next })) = iter.next() {
123 value += amount * helper(*next, haversack, cache);
124 }
125
126 cache[key] = Some(value);
127 value
128 }
129 }
130
131 let mut cache = vec![None; input.bags.len()];
132 helper(input.shiny_gold, input, &mut cache) - 1
133}