From 2a5bb55bee6fa3ace572bd596da80ea8aaf380e1 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Fri, 16 Dec 2022 07:56:58 -0800 Subject: [PATCH] Day 11, parts 1 + 2 --- 2022/day11/Cargo.lock | 92 +++++++++ 2022/day11/Cargo.toml | 9 + 2022/day11/example.txt | 27 +++ 2022/day11/input.txt | 55 +++++ 2022/day11/src/main.rs | 443 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 626 insertions(+) create mode 100644 2022/day11/Cargo.lock create mode 100644 2022/day11/Cargo.toml create mode 100644 2022/day11/example.txt create mode 100644 2022/day11/input.txt create mode 100644 2022/day11/src/main.rs diff --git a/2022/day11/Cargo.lock b/2022/day11/Cargo.lock new file mode 100644 index 0000000..a173758 --- /dev/null +++ b/2022/day11/Cargo.lock @@ -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", +] diff --git a/2022/day11/Cargo.toml b/2022/day11/Cargo.toml new file mode 100644 index 0000000..3643e3f --- /dev/null +++ b/2022/day11/Cargo.toml @@ -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" diff --git a/2022/day11/example.txt b/2022/day11/example.txt new file mode 100644 index 0000000..30e09e5 --- /dev/null +++ b/2022/day11/example.txt @@ -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 diff --git a/2022/day11/input.txt b/2022/day11/input.txt new file mode 100644 index 0000000..cff9230 --- /dev/null +++ b/2022/day11/input.txt @@ -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 diff --git a/2022/day11/src/main.rs b/2022/day11/src/main.rs new file mode 100644 index 0000000..ed1115b --- /dev/null +++ b/2022/day11/src/main.rs @@ -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 { + 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 { + 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>, + 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 { + 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, + operation: Option, + 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) -> 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 = 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 = Vec::new(); + let mut monkey_builder: Option = 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 = 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 = 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::>() + .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 = 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] + ); + } +}