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