2022-12-15 16:52:16 -05:00
|
|
|
use malachite::integer::Integer;
|
|
|
|
use malachite::num::arithmetic::traits::DivisibleBy;
|
|
|
|
use malachite::num::arithmetic::traits::Square;
|
|
|
|
use malachite::num::basic::traits::Zero;
|
2022-12-14 16:55:01 -05:00
|
|
|
use std::collections::VecDeque;
|
2022-12-15 11:00:41 -05:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
/// Just to cut down on boilerplate for operations
|
|
|
|
/// with primitive types
|
2022-12-15 16:52:16 -05:00
|
|
|
fn big(n: usize) -> Integer {
|
2022-12-15 11:00:41 -05:00
|
|
|
n.into()
|
|
|
|
}
|
2022-12-14 16:55:01 -05:00
|
|
|
|
2022-12-15 08:02:20 -05:00
|
|
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
2022-12-15 11:00:41 -05:00
|
|
|
pub enum WorryType {
|
2022-12-15 08:02:20 -05:00
|
|
|
Normal,
|
|
|
|
Extra,
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
|
|
enum Operand {
|
|
|
|
Old,
|
|
|
|
Literal(Integer)
|
|
|
|
}
|
|
|
|
|
2022-12-14 16:55:01 -05:00
|
|
|
#[derive(Debug)]
|
2022-12-15 16:52:16 -05:00
|
|
|
struct Operation {
|
|
|
|
operator: char,
|
|
|
|
operand: Operand,
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Operation {
|
2022-12-15 16:52:16 -05:00
|
|
|
fn new(operator: char, operand: &str) -> Self {
|
|
|
|
let operand = match operand {
|
|
|
|
"old" => Operand::Old,
|
|
|
|
_ => Operand::Literal(Integer::from_str(operand).unwrap()),
|
|
|
|
};
|
|
|
|
|
|
|
|
Operation {
|
|
|
|
operator,
|
|
|
|
operand,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 11:00:41 -05:00
|
|
|
#[inline(always)]
|
2022-12-15 16:52:16 -05:00
|
|
|
fn run(&self, old: Integer) -> Integer {
|
|
|
|
let operand = self.operand.clone();
|
|
|
|
if operand == Operand::Old && self.operator == '*' {
|
|
|
|
return old.square();
|
|
|
|
}
|
|
|
|
|
|
|
|
let other = match operand {
|
|
|
|
Operand::Old => old.clone(),
|
|
|
|
Operand::Literal(other) => other,
|
2022-12-14 16:55:01 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
match self.operator {
|
2022-12-15 16:52:16 -05:00
|
|
|
'+' => old + other,
|
|
|
|
'*' => old * other,
|
2022-12-14 16:55:01 -05:00
|
|
|
_ => panic!("Invalid operator"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-12-14 13:16:17 -05:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2022-12-15 11:00:41 -05:00
|
|
|
pub struct Monkey {
|
2022-12-15 16:52:16 -05:00
|
|
|
items: VecDeque<Integer>,
|
|
|
|
operation: Operation,
|
|
|
|
test: Integer,
|
|
|
|
pass_monkey: usize,
|
|
|
|
fail_monkey: usize,
|
|
|
|
inspection_count: Integer,
|
|
|
|
inspection_worry: WorryType,
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Monkey {
|
2022-12-15 11:00:41 -05:00
|
|
|
pub fn from_behavior(raw: &str, inspection_worry: WorryType) -> Self {
|
2022-12-14 16:55:01 -05:00
|
|
|
let lines: Vec<&str> = raw.lines().collect();
|
|
|
|
|
|
|
|
let item_parts: Vec<&str> = lines[1].split(": ").collect();
|
2022-12-15 16:52:16 -05:00
|
|
|
let items: VecDeque<Integer> = item_parts[1]
|
2022-12-14 16:55:01 -05:00
|
|
|
.split(", ")
|
2022-12-15 16:52:16 -05:00
|
|
|
.map(|i| i.parse::<Integer>().unwrap())
|
2022-12-14 16:55:01 -05:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
let op_parts: Vec<&str> = lines[2].split(" = ").collect();
|
|
|
|
let [_, operator, operand]: [&str; 3] = op_parts[1]
|
|
|
|
.split_ascii_whitespace()
|
|
|
|
.collect::<Vec<&str>>()
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let [test, pass_monkey, fail_monkey]: [usize; 3] = lines[3..]
|
|
|
|
.iter()
|
|
|
|
.map(|line| line.split_ascii_whitespace().collect::<Vec<&str>>())
|
|
|
|
.map(|parts| parts[parts.len() - 1])
|
|
|
|
.map(|n| n.parse::<usize>().unwrap())
|
|
|
|
.collect::<Vec<usize>>()
|
|
|
|
.try_into()
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
Monkey {
|
|
|
|
items,
|
2022-12-15 16:52:16 -05:00
|
|
|
operation: Operation::new(
|
|
|
|
operator.chars().next().unwrap(),
|
|
|
|
operand
|
|
|
|
),
|
|
|
|
test: big(test),
|
2022-12-14 16:55:01 -05:00
|
|
|
pass_monkey,
|
|
|
|
fail_monkey,
|
2022-12-15 16:52:16 -05:00
|
|
|
inspection_count: Integer::ZERO,
|
2022-12-15 08:02:20 -05:00
|
|
|
inspection_worry,
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
#[inline(always)]
|
|
|
|
fn run_test(&self, item: &Integer) -> usize {
|
|
|
|
if item.divisible_by(&self.test) {
|
2022-12-14 16:55:01 -05:00
|
|
|
self.pass_monkey
|
|
|
|
} else {
|
|
|
|
self.fail_monkey
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 11:00:41 -05:00
|
|
|
#[inline(always)]
|
2022-12-15 16:52:16 -05:00
|
|
|
pub fn inspect(&mut self, item: Integer) -> (usize, Integer) {
|
2022-12-15 11:00:41 -05:00
|
|
|
self.inspection_count += big(1);
|
2022-12-14 16:55:01 -05:00
|
|
|
|
2022-12-15 08:02:20 -05:00
|
|
|
let worry = if self.inspection_worry == WorryType::Normal {
|
2022-12-15 11:00:41 -05:00
|
|
|
self.operation.run(item) / big(3)
|
2022-12-15 08:02:20 -05:00
|
|
|
} else {
|
|
|
|
self.operation.run(item)
|
|
|
|
};
|
2022-12-14 16:55:01 -05:00
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
let new_monkey = self.run_test(&worry);
|
2022-12-14 16:55:01 -05:00
|
|
|
|
|
|
|
(new_monkey, worry)
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
#[inline(always)]
|
|
|
|
pub fn catch(&mut self, item: Integer) {
|
2022-12-14 16:55:01 -05:00
|
|
|
self.items.push_back(item);
|
|
|
|
}
|
2022-12-14 13:16:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2022-12-15 11:00:41 -05:00
|
|
|
pub struct MonkeyGame {
|
2022-12-15 16:52:16 -05:00
|
|
|
monkeys: Vec<Monkey>,
|
2022-12-14 13:16:17 -05:00
|
|
|
}
|
|
|
|
|
2022-12-14 16:55:01 -05:00
|
|
|
impl MonkeyGame {
|
2022-12-15 08:02:20 -05:00
|
|
|
pub fn from_file_str(file_str: &'static str, inspection_worry: WorryType) -> Self {
|
2022-12-14 16:55:01 -05:00
|
|
|
let behaviors = file_str.split("\n\n");
|
|
|
|
|
|
|
|
Self {
|
2022-12-15 08:02:20 -05:00
|
|
|
monkeys: behaviors
|
|
|
|
.map(|m| Monkey::from_behavior(m, inspection_worry))
|
|
|
|
.collect(),
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
fn throw(&mut self, item: Integer, to: usize) {
|
2022-12-14 16:55:01 -05:00
|
|
|
self.monkeys[to].catch(item);
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
#[inline(always)]
|
2022-12-15 11:00:41 -05:00
|
|
|
pub fn do_rounds(&mut self, rounds: usize) -> &Self {
|
|
|
|
for r in 0..rounds {
|
|
|
|
if r % 100 == 0 {
|
|
|
|
println!("Running round {}", r);
|
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
for m in 0..self.monkeys.len() {
|
|
|
|
while let Some(worry) = self.monkeys[m].items.pop_front() {
|
|
|
|
let (monkey_idx, worry) = self.monkeys[m].inspect(worry);
|
|
|
|
self.throw(worry, monkey_idx);
|
|
|
|
}
|
|
|
|
}
|
2022-12-14 22:01:54 -05:00
|
|
|
}
|
2022-12-15 11:00:41 -05:00
|
|
|
|
|
|
|
self
|
2022-12-14 22:01:54 -05:00
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
pub fn get_inspection_counts(&self) -> Vec<Integer> {
|
|
|
|
let mut counts: Vec<Integer> = self
|
2022-12-15 11:00:41 -05:00
|
|
|
.monkeys
|
|
|
|
.iter()
|
|
|
|
.map(|m| m.inspection_count.clone())
|
|
|
|
.collect();
|
2022-12-14 22:01:54 -05:00
|
|
|
|
|
|
|
counts.sort();
|
|
|
|
|
|
|
|
counts.into_iter().rev().collect()
|
|
|
|
}
|
2022-12-15 11:00:41 -05:00
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
pub fn get_monkey_business(&self) -> Integer {
|
2022-12-15 11:00:41 -05:00
|
|
|
let inspections = self.get_inspection_counts();
|
|
|
|
|
|
|
|
inspections.get(0).unwrap() * inspections.get(1).unwrap()
|
|
|
|
}
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|
|
|
|
|
2022-12-14 13:16:17 -05:00
|
|
|
fn main() {
|
|
|
|
let file_str = include_str!("input.txt");
|
2022-12-15 11:00:41 -05:00
|
|
|
let monkey_business1 = MonkeyGame::from_file_str(file_str, WorryType::Normal)
|
|
|
|
.do_rounds(20)
|
|
|
|
.get_monkey_business();
|
|
|
|
println!("Part 1 monkey business: {}", monkey_business1);
|
2022-12-14 22:01:54 -05:00
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
let monkey_business2 = MonkeyGame::from_file_str(file_str, WorryType::Extra)
|
|
|
|
.do_rounds(10_000)
|
2022-12-15 11:00:41 -05:00
|
|
|
.get_monkey_business();
|
2022-12-15 16:52:16 -05:00
|
|
|
println!("Part 2 monkey business: {}", monkey_business2);
|
2022-12-14 13:16:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
fn get_test_data() -> &'static str {
|
|
|
|
include_str!("test-input.txt")
|
|
|
|
}
|
2022-12-14 16:55:01 -05:00
|
|
|
|
|
|
|
#[test]
|
2022-12-15 16:52:16 -05:00
|
|
|
fn monkey_round() {
|
2022-12-15 08:02:20 -05:00
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Normal);
|
2022-12-15 16:52:16 -05:00
|
|
|
game.do_rounds(1);
|
2022-12-14 16:55:01 -05:00
|
|
|
|
2022-12-15 11:00:41 -05:00
|
|
|
assert_eq!(
|
|
|
|
game.monkeys[0].items,
|
|
|
|
VecDeque::from([big(20), big(23), big(27), big(26)])
|
|
|
|
);
|
2022-12-14 16:55:01 -05:00
|
|
|
assert_eq!(
|
|
|
|
game.monkeys[1].items,
|
2022-12-15 11:00:41 -05:00
|
|
|
VecDeque::from([
|
|
|
|
big(2080),
|
|
|
|
big(25),
|
|
|
|
big(167),
|
|
|
|
big(207),
|
|
|
|
big(401),
|
|
|
|
big(1046)
|
|
|
|
])
|
2022-12-14 16:55:01 -05:00
|
|
|
);
|
|
|
|
assert_eq!(game.monkeys[2].items, VecDeque::new());
|
|
|
|
assert_eq!(game.monkeys[3].items, VecDeque::new());
|
|
|
|
}
|
2022-12-14 22:01:54 -05:00
|
|
|
|
|
|
|
#[test]
|
2022-12-15 16:52:16 -05:00
|
|
|
fn monkey_20_rounds() {
|
2022-12-15 08:02:20 -05:00
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Normal);
|
2022-12-14 22:01:54 -05:00
|
|
|
game.do_rounds(20);
|
|
|
|
|
2022-12-15 11:00:41 -05:00
|
|
|
assert_eq!(game.monkeys[0].inspection_count, big(101));
|
|
|
|
assert_eq!(game.monkeys[3].inspection_count, big(105));
|
2022-12-15 16:52:16 -05:00
|
|
|
assert_eq!(game.get_monkey_business(), big(10605));
|
2022-12-15 11:00:41 -05:00
|
|
|
}
|
|
|
|
|
2022-12-15 16:52:16 -05:00
|
|
|
fn monkey_10_000_rounds() {
|
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Normal);
|
|
|
|
game.do_rounds(10_000);
|
|
|
|
|
|
|
|
assert_eq!(game.monkeys[0].inspection_count, big(52166));
|
|
|
|
assert_eq!(game.monkeys[3].inspection_count, big(52013));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn monkey_20_rounds_extra_worry() {
|
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Extra);
|
|
|
|
game.do_rounds(20);
|
|
|
|
|
|
|
|
assert_eq!(game.monkeys[0].inspection_count, big(99));
|
|
|
|
assert_eq!(game.monkeys[3].inspection_count, big(103));
|
|
|
|
assert_eq!(game.get_monkey_business(), big(10197));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn monkey_1000_rounds_extra_worry() {
|
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Extra);
|
|
|
|
game.do_rounds(1000);
|
|
|
|
|
|
|
|
assert_eq!(game.monkeys[0].inspection_count, big(5204));
|
|
|
|
assert_eq!(game.monkeys[3].inspection_count, big(5192));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn monkey_10_000_rounds_extra_worry() {
|
2022-12-15 11:00:41 -05:00
|
|
|
let mut game = MonkeyGame::from_file_str(get_test_data(), WorryType::Extra);
|
|
|
|
game.do_rounds(10_000);
|
|
|
|
|
|
|
|
assert_eq!(game.monkeys[0].inspection_count, big(52166));
|
|
|
|
assert_eq!(game.monkeys[3].inspection_count, big(52013));
|
2022-12-14 22:01:54 -05:00
|
|
|
}
|
2022-12-14 16:55:01 -05:00
|
|
|
}
|