Day 11, parts 1 + 2

This commit is contained in:
Eryn Wells 2022-12-16 07:56:58 -08:00
parent dafb8e35b4
commit 2a5bb55bee
5 changed files with 626 additions and 0 deletions

92
2022/day11/Cargo.lock generated Normal file
View file

@ -0,0 +1,92 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "day11"
version = "0.1.0"
dependencies = [
"num",
]
[[package]]
name = "num"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]

9
2022/day11/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "day11"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num = "0.4.0"

27
2022/day11/example.txt Normal file
View file

@ -0,0 +1,27 @@
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1

55
2022/day11/input.txt Normal file
View file

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 54, 82, 90, 88, 86, 54
Operation: new = old * 7
Test: divisible by 11
If true: throw to monkey 2
If false: throw to monkey 6
Monkey 1:
Starting items: 91, 65
Operation: new = old * 13
Test: divisible by 5
If true: throw to monkey 7
If false: throw to monkey 4
Monkey 2:
Starting items: 62, 54, 57, 92, 83, 63, 63
Operation: new = old + 1
Test: divisible by 7
If true: throw to monkey 1
If false: throw to monkey 7
Monkey 3:
Starting items: 67, 72, 68
Operation: new = old * old
Test: divisible by 2
If true: throw to monkey 0
If false: throw to monkey 6
Monkey 4:
Starting items: 68, 89, 90, 86, 84, 57, 72, 84
Operation: new = old + 7
Test: divisible by 17
If true: throw to monkey 3
If false: throw to monkey 5
Monkey 5:
Starting items: 79, 83, 64, 58
Operation: new = old + 6
Test: divisible by 13
If true: throw to monkey 3
If false: throw to monkey 0
Monkey 6:
Starting items: 96, 72, 89, 70, 88
Operation: new = old + 4
Test: divisible by 3
If true: throw to monkey 1
If false: throw to monkey 2
Monkey 7:
Starting items: 79
Operation: new = old + 8
Test: divisible by 19
If true: throw to monkey 4
If false: throw to monkey 5

443
2022/day11/src/main.rs Normal file
View file

@ -0,0 +1,443 @@
use std::cell::RefCell;
use std::{env, fs};
#[allow(dead_code)]
#[derive(Clone, Copy, Debug, PartialEq)]
enum Verbosity {
None,
Full,
}
#[derive(Clone, Debug, PartialEq)]
enum Term {
Old,
Fixed(i64),
}
impl TryFrom<&str> for Term {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
if s == "old" {
Ok(Term::Old)
} else if let Ok(n) = i64::from_str_radix(s, 10) {
Ok(Term::Fixed(n))
} else {
Err(format!("Unable to parse {s} as Term"))
}
}
}
#[derive(Clone, Debug)]
struct Operation {
left: Term,
operator: Operator,
right: Term,
}
impl Operation {
fn perform(&self, item: &i64, verbosity: Verbosity) -> i64 {
assert!(self.left == Term::Old);
match self.operator {
Operator::Add => match self.right {
Term::Old => {
let result = item + item;
if verbosity == Verbosity::Full {
println!(" Worry level increases by itself to {result}.");
}
item + item
}
Term::Fixed(value) => {
let result = item + value;
if verbosity == Verbosity::Full {
println!(" Worry level increases by {value} to {result}.");
}
result
}
},
Operator::Sub => match self.right {
Term::Old => {
let result = item - item;
if verbosity == Verbosity::Full {
println!(" Worry level decreases by itself to {result}.");
}
result
}
Term::Fixed(value) => {
let result = item - value;
if verbosity == Verbosity::Full {
println!(" Worry level decreases by {value} to {result}.");
}
result
}
},
Operator::Mul => match self.right {
Term::Old => {
let result = item * item;
if verbosity == Verbosity::Full {
println!(" Worry level is multiplied by itself to {result}.");
}
result
}
Term::Fixed(value) => {
let result = item * value;
if verbosity == Verbosity::Full {
println!(" Worry level is multiplied by {value} to {result}.");
}
result
}
},
Operator::Div => match self.right {
Term::Old => {
let result = item / item;
if verbosity == Verbosity::Full {
println!(" Worry level is divided by itself to {result}.");
}
result
}
Term::Fixed(value) => {
let result = item / value;
if verbosity == Verbosity::Full {
println!(" Worry level is divided by {value} to {result}.");
}
result
}
},
}
}
}
impl Operation {
fn new(left: Term, operator: Operator, right: Term) -> Operation {
Operation {
left,
operator,
right,
}
}
}
#[derive(Clone, Debug)]
enum Operator {
Add,
Sub,
Mul,
Div,
}
impl TryFrom<&str> for Operator {
type Error = String;
fn try_from(s: &str) -> Result<Self, Self::Error> {
match s {
"+" => Ok(Operator::Add),
"-" => Ok(Operator::Sub),
"*" => Ok(Operator::Mul),
"/" => Ok(Operator::Div),
_ => Err(format!("Unable to format {s} as Operator")),
}
}
}
struct InspectionReport {
target_monkey: usize,
item: i64,
}
impl InspectionReport {
fn new(target_monkey: usize, item: i64) -> InspectionReport {
InspectionReport {
target_monkey,
item,
}
}
}
#[derive(Clone, Debug)]
struct Monkey {
n: usize,
items: RefCell<Vec<i64>>,
operation: Operation,
test_divisor: i64,
target_monkey_if_true: usize,
target_monkey_if_false: usize,
}
impl Monkey {
fn take_item(&self, item: i64) {
self.items.borrow_mut().push(item);
}
fn inspect_items(
&self,
with_anxiety_easying: bool,
verbosity: Verbosity,
) -> Vec<InspectionReport> {
if verbosity == Verbosity::Full {
println!("Monkey {}:", self.n);
}
let reports = self
.items
.borrow()
.iter()
.map(|item| self._inspect_item(item, with_anxiety_easying, verbosity))
.collect();
self.items.borrow_mut().clear();
reports
}
fn _inspect_item(
&self,
item: &i64,
should_ease_anxiety: bool,
verbosity: Verbosity,
) -> InspectionReport {
if verbosity == Verbosity::Full {
println!(" Monkey inspects an item with a worry level of {}", item);
}
let mut modified_worry_level = self.operation.perform(item, verbosity);
if should_ease_anxiety {
if verbosity == Verbosity::Full {
println!(
" Monkey gets bored with item. Worry level is divided by 3 to {}",
modified_worry_level
);
}
modified_worry_level = modified_worry_level / 3;
} else {
modified_worry_level = modified_worry_level % 9699690;
if verbosity == Verbosity::Full {
println!(
" Monkey gets bored with item. Normalizing worry level to {}",
modified_worry_level
);
}
}
if modified_worry_level % self.test_divisor == 0 {
if verbosity == Verbosity::Full {
println!(
" Current worry level is divisible by {}",
self.test_divisor
);
println!(
" Item with worry level {} is thrown to {}",
modified_worry_level, self.target_monkey_if_true
);
}
InspectionReport::new(self.target_monkey_if_true, modified_worry_level)
} else {
if verbosity == Verbosity::Full {
println!(
" Current worry level is not divisible by {}",
self.test_divisor
);
println!(
" Item with worry level {} is thrown to {}",
modified_worry_level, self.target_monkey_if_false
);
}
InspectionReport::new(self.target_monkey_if_false, modified_worry_level)
}
}
}
struct MonkeyBuilder {
n: usize,
items: Vec<i64>,
operation: Option<Operation>,
test_divisor: i64,
target_monkey_if_true: usize,
target_monkey_if_false: usize,
}
impl MonkeyBuilder {
fn new(n: usize) -> MonkeyBuilder {
MonkeyBuilder {
n,
items: Vec::new(),
operation: None,
test_divisor: 0,
target_monkey_if_true: 0,
target_monkey_if_false: 0,
}
}
fn build(self) -> Monkey {
Monkey {
n: self.n,
items: RefCell::new(self.items),
operation: self.operation.unwrap(),
test_divisor: self.test_divisor,
target_monkey_if_true: self.target_monkey_if_true,
target_monkey_if_false: self.target_monkey_if_false,
}
}
fn items(mut self, items: Vec<i64>) -> MonkeyBuilder {
self.items = items;
self
}
fn operation(mut self, operation: Operation) -> MonkeyBuilder {
self.operation = Some(operation);
self
}
fn test_divisor(mut self, divisor: i64) -> MonkeyBuilder {
self.test_divisor = divisor;
self
}
fn target_monkey_if_true(mut self, target_monkey: usize) -> MonkeyBuilder {
self.target_monkey_if_true = target_monkey;
self
}
fn target_monkey_if_false(mut self, target_monkey: usize) -> MonkeyBuilder {
self.target_monkey_if_false = target_monkey;
self
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let filename = args.get(1).expect("Missing filename");
let file_contents = fs::read_to_string(filename).expect("Unable to read {filename}");
let mut monkeys: Vec<Monkey> = Vec::new();
let mut monkey_builder: Option<MonkeyBuilder> = None;
for line in file_contents.lines() {
if line.starts_with("Monkey") {
let split_line: Vec<&str> = line.split(&[' ', ':'][..]).collect();
let n = usize::from_str_radix(split_line[1], 10).unwrap();
assert!(monkey_builder.is_none());
monkey_builder = Some(MonkeyBuilder::new(n));
} else if line.starts_with(" Starting items:") {
let mut split = line.split(": ").skip(1);
let items: Vec<i64> = split
.next()
.unwrap()
.split(", ")
.map(|s| i64::from_str_radix(s, 10).unwrap())
.collect();
monkey_builder = Some(monkey_builder.unwrap().items(items));
} else if line.starts_with(" Operation:") {
let mut split = line.split(": ").skip(1);
let operation_terms: Vec<&str> = split.next().unwrap().split(" ").collect();
let left_term = Term::try_from(operation_terms[2]).unwrap();
let right_term = Term::try_from(operation_terms[4]).unwrap();
let operator = Operator::try_from(operation_terms[3]).unwrap();
let operation = Operation::new(left_term, operator, right_term);
monkey_builder = Some(monkey_builder.unwrap().operation(operation));
} else if line.starts_with(" Test:") {
let mut split = line.split(": ").skip(1);
let divisor =
i64::from_str_radix(split.next().unwrap().split(" ").skip(2).next().unwrap(), 10)
.unwrap();
monkey_builder = Some(monkey_builder.unwrap().test_divisor(divisor));
} else if line.starts_with(" If true:") {
let split: Vec<&str> = line.split(" ").collect();
let target_monkey = usize::from_str_radix(split.last().unwrap(), 10).unwrap();
monkey_builder = Some(monkey_builder.unwrap().target_monkey_if_true(target_monkey));
} else if line.starts_with(" If false:") {
let split: Vec<&str> = line.split(" ").collect();
let target_monkey = usize::from_str_radix(split.last().unwrap(), 10).unwrap();
monkey_builder = Some(
monkey_builder
.unwrap()
.target_monkey_if_false(target_monkey),
);
} else if line == "" {
let builder = monkey_builder.take();
monkeys.push(builder.unwrap().build())
}
}
if let Some(builder) = monkey_builder.take() {
monkeys.push(builder.build());
}
{
let part1_monkeys = monkeys.clone();
let mut part1_monkey_inspection_counts: Vec<u32> = vec![0; part1_monkeys.len()];
for round in 1..=20 {
println!("----- Round {round} -----");
for monkey in &part1_monkeys {
let reports = monkey.inspect_items(true, Verbosity::Full);
part1_monkey_inspection_counts[monkey.n] += reports.len() as u32;
for r in reports {
part1_monkeys[r.target_monkey].take_item(r.item);
}
}
println!("After round {round}, the monkeys are holding items with these worry levels:");
for monkey in &part1_monkeys {
println!(
"Monkey {}: {}",
monkey.n,
monkey
.items
.borrow()
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>()
.join(", ")
);
}
}
println!("----- Final Counts -----");
for (i, c) in part1_monkey_inspection_counts.iter().enumerate() {
println!("Monkey {i} inspected items {c} times.");
}
let mut part1_sorted_counts = part1_monkey_inspection_counts.clone();
part1_sorted_counts.sort_by(|a, b| b.cmp(a));
println!(
"Part 1: monkey business: {} * {} = {}",
part1_sorted_counts[0],
part1_sorted_counts[1],
part1_sorted_counts[0] * part1_sorted_counts[1]
);
}
{
let part2_monkeys = monkeys.clone();
let mut part2_monkey_inspection_counts: Vec<u64> = vec![0; part2_monkeys.len()];
for round in 1..=10000 {
println!("----- Round {round} -----");
for monkey in &part2_monkeys {
let reports = monkey.inspect_items(false, Verbosity::None);
part2_monkey_inspection_counts[monkey.n] += reports.len() as u64;
for r in reports {
part2_monkeys[r.target_monkey].take_item(r.item);
}
}
for (i, c) in part2_monkey_inspection_counts.iter().enumerate() {
println!("Monkey {i} inspected items {c} times.");
}
}
let mut part2_sorted_counts = part2_monkey_inspection_counts.clone();
part2_sorted_counts.sort_by(|a, b| b.cmp(a));
println!(
"Part 2: monkey business: {} * {} = {}",
part2_sorted_counts[0],
part2_sorted_counts[1],
part2_sorted_counts[0] * part2_sorted_counts[1]
);
}
}