Day 11, parts 1 + 2
This commit is contained in:
parent
dafb8e35b4
commit
2a5bb55bee
5 changed files with 626 additions and 0 deletions
92
2022/day11/Cargo.lock
generated
Normal file
92
2022/day11/Cargo.lock
generated
Normal 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
9
2022/day11/Cargo.toml
Normal 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
27
2022/day11/example.txt
Normal 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
55
2022/day11/input.txt
Normal 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
443
2022/day11/src/main.rs
Normal 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]
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue