1use std::array::from_fn;
47use std::ops::{Add, Mul, Sub};
48
49use crate::util::hash::*;
50use crate::util::parse::*;
51use crate::util::thread::*;
52
53type Input = (Vec<Monkey>, Vec<Pair>);
54type Pair = (usize, usize);
55
56pub struct Monkey {
57 items: Vec<usize>,
58 operation: Operation,
59 test: usize,
60 yes: usize,
61 no: usize,
62}
63
64impl Monkey {
65 fn inspect(&self, item: usize) -> usize {
67 match self.operation {
68 Operation::Square => item * item,
69 Operation::Multiply(y) => item * y,
70 Operation::Add(y) => item + y,
71 }
72 }
73
74 fn throw(&self, item: usize) -> usize {
76 if item.is_multiple_of(self.test) { self.yes } else { self.no }
77 }
78}
79
80enum Operation {
81 Square,
82 Multiply(usize),
83 Add(usize),
84}
85
86#[derive(Clone, Copy, Default)]
87struct Business([usize; 8]);
88
89impl Business {
90 fn inc(&mut self, from: usize) {
91 self.0[from] += 1;
92 }
93
94 fn level(mut self) -> usize {
95 self.0.sort_unstable();
96 self.0.iter().rev().take(2).product()
97 }
98}
99
100impl Add for Business {
102 type Output = Self;
103
104 #[inline]
105 fn add(self, rhs: Self) -> Self {
106 Self(from_fn(|i| self.0[i] + rhs.0[i]))
107 }
108}
109
110impl Sub for Business {
111 type Output = Self;
112
113 #[inline]
114 fn sub(self, rhs: Self) -> Self {
115 Self(from_fn(|i| self.0[i] - rhs.0[i]))
116 }
117}
118
119impl Mul<usize> for Business {
120 type Output = Self;
121
122 #[inline]
123 fn mul(self, rhs: usize) -> Self {
124 Self(self.0.map(|n| n * rhs))
125 }
126}
127
128pub fn parse(input: &str) -> Input {
131 let lines: Vec<_> = input.lines().collect();
132
133 let monkeys: Vec<_> = lines
134 .chunks(7)
135 .map(|chunk| {
136 let items = chunk[1].iter_unsigned().collect();
137 let tokens: Vec<_> = chunk[2].rsplit(' ').take(2).collect();
138 let operation = match tokens[..] {
139 ["old", _] => Operation::Square,
140 [y, "*"] => Operation::Multiply(y.unsigned()),
141 [y, "+"] => Operation::Add(y.unsigned()),
142 _ => unreachable!(),
143 };
144 let test = chunk[3].unsigned();
145 let yes = chunk[4].unsigned();
146 let no = chunk[5].unsigned();
147 Monkey { items, operation, test, yes, no }
148 })
149 .collect();
150
151 let pairs = monkeys
152 .iter()
153 .enumerate()
154 .flat_map(|(from, monkey)| monkey.items.iter().map(move |&item| (from, item)))
155 .collect();
156
157 (monkeys, pairs)
158}
159
160pub fn part1(input: &Input) -> usize {
161 let (monkeys, pairs) = input;
162 let mut business = Business::default();
163
164 for &(mut from, mut item) in pairs {
165 let mut rounds = 0;
166
167 while rounds < 20 {
168 item = monkeys[from].inspect(item) / 3;
169 let to = monkeys[from].throw(item);
170
171 business.inc(from);
172
173 rounds += usize::from(to < from);
176 from = to;
177 }
178 }
179
180 business.level()
181}
182
183pub fn part2(input: &Input) -> usize {
184 let (monkeys, pairs) = input;
185
186 let result = spawn_parallel_iterator(pairs, |iter| {
188 iter.map(|&(from, item)| play(monkeys, from, item)).collect::<Vec<_>>()
189 });
190
191 result.into_iter().flatten().fold(Business::default(), Business::add).level()
193}
194
195fn play(monkeys: &[Monkey], mut from: usize, mut item: usize) -> Business {
198 let product: usize = monkeys.iter().map(|m| m.test).product();
199
200 let mut round = 0;
201 let mut business = Business::default();
202
203 let mut path = vec![business];
204 let mut seen = FastMap::build([((from, item), round)]);
205
206 while round < 10_000 {
207 item = monkeys[from].inspect(item) % product;
208 let to = monkeys[from].throw(item);
209
210 business.inc(from);
211
212 if to < from {
215 round += 1;
216 path.push(business);
217
218 if let Some(previous) = seen.insert((to, item), round) {
220 let cycle_width = round - previous;
221
222 let offset = 10_000 - round;
223 let quotient = offset / cycle_width;
224 let remainder = offset % cycle_width;
225
226 let full = (business - path[previous]) * quotient;
227 let partial = path[previous + remainder] - path[previous];
228 return business + full + partial;
229 }
230 }
231
232 from = to;
233 }
234
235 business
236}