From 77e443e6c82c0545cbcfda4345e6a3e6742d55ff Mon Sep 17 00:00:00 2001 From: Xiretza Date: Sun, 11 Dec 2022 09:39:15 +0100 Subject: [PATCH] 2022 day11/rust: add solution --- 2022/day11/rust/Cargo.toml | 9 ++ 2022/day11/rust/src/main.rs | 191 ++++++++++++++++++++++++++++++++++++ Cargo.lock | 7 ++ Cargo.toml | 1 + 4 files changed, 208 insertions(+) create mode 100644 2022/day11/rust/Cargo.toml create mode 100644 2022/day11/rust/src/main.rs diff --git a/2022/day11/rust/Cargo.toml b/2022/day11/rust/Cargo.toml new file mode 100644 index 0000000..9299e6f --- /dev/null +++ b/2022/day11/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust_2022_11" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +aoc = { path = "../../../common/rust" } diff --git a/2022/day11/rust/src/main.rs b/2022/day11/rust/src/main.rs new file mode 100644 index 0000000..e0522c0 --- /dev/null +++ b/2022/day11/rust/src/main.rs @@ -0,0 +1,191 @@ +#![warn(clippy::pedantic)] + +use std::{ + cmp::Reverse, + io::{stdin, Read}, + ops::Rem, + str::FromStr, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Operand { + Const(usize), + Old, +} + +impl Operand { + fn evaluate(self, old: usize) -> usize { + match self { + Self::Old => old, + Self::Const(c) => c, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Operation { + Add(Operand), + Multiply(Operand), + Divide(Operand), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Test { + Divisible(usize), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Item { + value: usize, +} + +impl Item { + fn apply_operation(&mut self, op: Operation, modulo: Option) { + match op { + Operation::Add(x) => self.value += x.evaluate(self.value), + Operation::Multiply(x) => self.value *= x.evaluate(self.value), + Operation::Divide(x) => { + assert!(modulo.is_none(), "can't divide under modulo arithmetic"); + self.value /= x.evaluate(self.value); + } + } + + if let Some(modulo) = modulo { + self.value %= modulo; + } + } + + fn apply_test(&self, test: Test) -> bool { + match test { + Test::Divisible(n) => self.value.rem(n) == 0, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct Monkey { + items: Vec, + operation: Operation, + test: Test, + target_pass: usize, + target_fail: usize, + inspections: usize, +} + +impl Monkey { + fn throw_item(&mut self, divisor: Option, modulo: Option) -> (usize, Item) { + self.inspections += 1; + let mut item = self.items.remove(0); + item.apply_operation(self.operation, modulo); + if let Some(divisor) = divisor { + item.apply_operation(Operation::Divide(Operand::Const(divisor)), modulo); + } + + let target = if item.apply_test(self.test) { + self.target_pass + } else { + self.target_fail + }; + + (target, item) + } +} + +impl FromStr for Monkey { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut lines = s.lines(); + lines.next().unwrap(); + let items: Vec<_> = lines + .next() + .unwrap() + .split_once(':') + .unwrap() + .1 + .trim() + .split(", ") + .map(|i| i.parse().unwrap()) + .map(|value| Item { value }) + .collect(); + + let op: Vec<_> = lines.next().unwrap().split_whitespace().collect(); + let (operand, op) = op.split_last().unwrap(); + let (operator, _) = op.split_last().unwrap(); + let operand = match *operand { + "old" => Operand::Old, + i => Operand::Const(i.parse().unwrap()), + }; + let operation = match *operator { + "+" => Operation::Add(operand), + "*" => Operation::Multiply(operand), + _ => panic!("invalid operator {operator}"), + }; + + let mut lastnum = || { + lines + .next() + .unwrap() + .split_whitespace() + .last() + .unwrap() + .parse() + .unwrap() + }; + let test = Test::Divisible(lastnum()); + + let target_pass = lastnum(); + let target_fail = lastnum(); + + Ok(Monkey { + items, + operation, + test, + target_pass, + target_fail, + inspections: 0, + }) + } +} + +fn play(monkeys: &mut [Monkey], rounds: usize, divisor: Option) -> usize { + let modulo = match divisor { + Some(_) => None, + None => Some( + monkeys + .iter() + .map(|m| { + let Test::Divisible(i) = m.test; + i + }) + .product(), + ), + }; + + for _round in 0..rounds { + for i in 0..monkeys.len() { + for _j in 0..monkeys[i].items.len() { + let (target, item) = monkeys[i].throw_item(divisor, modulo); + + monkeys[target].items.push(item); + } + } + } + + let mut levels: Vec<_> = monkeys.iter().map(|m| m.inspections).collect(); + levels.sort_unstable_by_key(|i| Reverse(*i)); + levels[0] * levels[1] +} + +fn main() { + let mut data = String::new(); + stdin().read_to_string(&mut data).unwrap(); + + let mut monkeys: Vec = data + .split("\n\n") + .map(|monkey| monkey.parse().unwrap()) + .collect(); + + println!("{:?}", play(&mut monkeys.clone(), 20, Some(3))); + println!("{:?}", play(&mut monkeys, 10_000, None)); +} diff --git a/Cargo.lock b/Cargo.lock index 1dfd6d4..4a04c69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -538,6 +538,13 @@ dependencies = [ "aoc", ] +[[package]] +name = "rust_2022_11" +version = "0.1.0" +dependencies = [ + "aoc", +] + [[package]] name = "ryu" version = "1.0.11" diff --git a/Cargo.toml b/Cargo.toml index 0b8679f..46ed02e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,5 @@ members = [ "2022/day8/rust", "2022/day9/rust", "2022/day10/rust", + "2022/day11/rust", ]