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