From e7a06642aa42b69259913b651ac946d4557e2823 Mon Sep 17 00:00:00 2001 From: Tobias Berger Date: Mon, 12 Dec 2022 16:15:14 +0100 Subject: [PATCH] Rust Day 11 --- rust/Cargo.lock | 2 +- rust/Cargo.toml | 6 +- rust/src/day11/input.txt | 55 ++++++++++++ rust/src/day11/main.rs | 144 ++++++++++++++++++++++++++++++++ rust/src/day11/part_1.rs | 48 +++++++++++ rust/src/day11/part_2.rs | 57 +++++++++++++ rust/src/day11/sample_input.txt | 27 ++++++ 7 files changed, 337 insertions(+), 2 deletions(-) create mode 100644 rust/src/day11/input.txt create mode 100644 rust/src/day11/main.rs create mode 100644 rust/src/day11/part_1.rs create mode 100644 rust/src/day11/part_2.rs create mode 100644 rust/src/day11/sample_input.txt diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 5bfe0c3..b0bb3d7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "advent-of-code" -version = "22.8.2" +version = "22.11.2" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 8ddebbd..9a2278c 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "advent-of-code" -version = "22.8.2" +version = "22.11.2" edition = "2021" resolver = "2" @@ -63,6 +63,10 @@ path = "src/day09/main.rs" name = "day10" path = "src/day10/main.rs" +[[bin]] +name = "day11" +path = "src/day11/main.rs" + [dependencies] # Required for one of the day06 solutions (commented out) # phf = { version = "0.11.1", features = ["macros"] } \ No newline at end of file diff --git a/rust/src/day11/input.txt b/rust/src/day11/input.txt new file mode 100644 index 0000000..29c7508 --- /dev/null +++ b/rust/src/day11/input.txt @@ -0,0 +1,55 @@ +Monkey 0: + Starting items: 52, 78, 79, 63, 51, 94 + Operation: new = old * 13 + Test: divisible by 5 + If true: throw to monkey 1 + If false: throw to monkey 6 + +Monkey 1: + Starting items: 77, 94, 70, 83, 53 + Operation: new = old + 3 + Test: divisible by 7 + If true: throw to monkey 5 + If false: throw to monkey 3 + +Monkey 2: + Starting items: 98, 50, 76 + Operation: new = old * old + Test: divisible by 13 + If true: throw to monkey 0 + If false: throw to monkey 6 + +Monkey 3: + Starting items: 92, 91, 61, 75, 99, 63, 84, 69 + Operation: new = old + 5 + Test: divisible by 11 + If true: throw to monkey 5 + If false: throw to monkey 7 + +Monkey 4: + Starting items: 51, 53, 83, 52 + Operation: new = old + 7 + Test: divisible by 3 + If true: throw to monkey 2 + If false: throw to monkey 0 + +Monkey 5: + Starting items: 76, 76 + Operation: new = old + 4 + Test: divisible by 2 + If true: throw to monkey 4 + If false: throw to monkey 7 + +Monkey 6: + Starting items: 75, 59, 93, 69, 76, 96, 65 + Operation: new = old * 19 + Test: divisible by 17 + If true: throw to monkey 1 + If false: throw to monkey 3 + +Monkey 7: + Starting items: 89 + Operation: new = old + 2 + Test: divisible by 19 + If true: throw to monkey 2 + If false: throw to monkey 4 \ No newline at end of file diff --git a/rust/src/day11/main.rs b/rust/src/day11/main.rs new file mode 100644 index 0000000..daa1d17 --- /dev/null +++ b/rust/src/day11/main.rs @@ -0,0 +1,144 @@ +const INPUT: &str = include_str!("input.txt"); + +mod part_1; +use part_1::part_1; +mod part_2; +use part_2::part_2; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) enum Op { + Add(u64), + Mul(u64), + Square, +} +impl Op { + fn perform(&self, worry: u64) -> u64 { + match self { + Op::Add(a) => a + worry, + Op::Mul(a) => a * worry, + Op::Square => worry.pow(2), + } + } +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct Test { + pub divisible_by: u64, + pub if_true: usize, + pub if_false: usize, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub(crate) struct Monkey { + pub id: usize, + pub items: Vec, + pub operation: Op, + pub test: Test, +} + +pub(crate) fn parse_input(input: &'static str) -> Vec { + let normalized_line_endings = input.replace('\r', ""); + let monkeys = normalized_line_endings.split("\n\n"); + + let mut result = Vec::new(); + for monkey in monkeys { + let mut chars = monkey.chars(); + + for _ in 0.."Monkey ".len() { + chars.next(); + } + let id_string = chars + .by_ref() + .take_while(|char| !matches!(char, ':')) + .collect::(); + + for _ in 0.."\n Starting items: ".len() { + chars.next(); + } + let starting_items_string = chars + .by_ref() + .take_while(|char| !matches!(char, '\n')) + .collect::(); + + for _ in 0.." Operation: new = old ".len() { + chars.next(); + } + let operation_string = chars + .by_ref() + .take_while(|char| !matches!(char, '\n')) + .collect::(); + + for _ in 0.." Test: divisible by ".len() { + chars.next(); + } + let divisible_by_string = chars + .by_ref() + .take_while(|char| matches!(char, '-' | '0'..='9')) + .collect::(); + + for _ in 0.." If true: throw to monkey ".len() { + chars.next(); + } + let if_true_string = chars + .by_ref() + .take_while(|char| char.is_numeric()) + .collect::(); + + for _ in 0.." If false: throw to monkey ".len() { + chars.next(); + } + let if_false_string = chars + .by_ref() + .take_while(|char| char.is_numeric()) + .collect::(); + + let id = id_string.parse().unwrap(); + let starting_items = starting_items_string + .split(", ") + .map(|num_string| num_string.parse().unwrap()) + .collect(); + let operation = if operation_string.starts_with('+') { + Op::Add( + operation_string + .chars() + .skip(2) + .collect::() + .parse() + .unwrap(), + ) + } else if operation_string.chars().nth(2) == Some('o') { + Op::Square + } else { + Op::Mul( + operation_string + .chars() + .skip(2) + .collect::() + .parse() + .unwrap(), + ) + }; + let divisible_by = divisible_by_string.parse().unwrap(); + let if_true = if_true_string.parse().expect("Non-numeric if_true?"); + let if_false = if_false_string.parse().unwrap(); + let test = Test { + divisible_by, + if_true, + if_false, + }; + result.push(Monkey { + id, + items: starting_items, + operation, + test, + }) + } + + result +} + +pub fn main() { + let input = parse_input(INPUT); + part_1(&input); + part_2(&input); +} diff --git a/rust/src/day11/part_1.rs b/rust/src/day11/part_1.rs new file mode 100644 index 0000000..9efddb6 --- /dev/null +++ b/rust/src/day11/part_1.rs @@ -0,0 +1,48 @@ +use crate::Monkey; + +const ROUND_COUNT: usize = 20; + +pub(crate) fn part_1(input: &[Monkey]) -> u64 { + let mut monkey_state = input.to_owned(); + let mut times_inspected = vec![0; monkey_state.len()]; + for _ in 0..ROUND_COUNT { + for monkey_idx in 0..monkey_state.len() { + while let Some(worry) = monkey_state[monkey_idx].items.pop() { + let worry = monkey_state[monkey_idx].operation.perform(worry) / 3; + + let throw_to = if worry % monkey_state[monkey_idx].test.divisible_by == 0 { + monkey_state[monkey_idx].test.if_true + } else { + monkey_state[monkey_idx].test.if_false + }; + + monkey_state[throw_to].items.push(worry); + + times_inspected[monkey_idx] += 1; + } + } + } + + times_inspected.sort_unstable(); + times_inspected.reverse(); + let result = times_inspected[0] * times_inspected[1]; + + println!("Part 1: {result}"); + + result +} + +#[cfg(test)] +mod tests { + const SAMPLE_INPUT: &str = include_str!("sample_input.txt"); + + #[test] + fn test_with_solution() { + assert_eq!(super::part_1(&crate::parse_input(crate::INPUT)), 58786); + } + + #[test] + fn test_with_sample_solution() { + assert_eq!(super::part_1(&crate::parse_input(SAMPLE_INPUT)), 10605); + } +} diff --git a/rust/src/day11/part_2.rs b/rust/src/day11/part_2.rs new file mode 100644 index 0000000..fbacfe1 --- /dev/null +++ b/rust/src/day11/part_2.rs @@ -0,0 +1,57 @@ +use crate::Monkey; + +const ROUND_COUNT: usize = 10_000; + +pub(crate) fn part_2(input: &[Monkey]) -> u64 { + let mut monkey_state = input.to_owned(); + let mut times_inspected = vec![0; monkey_state.len()]; + + let lcm = monkey_state + .iter() + .map(|monkey| monkey.test.divisible_by) + .product::(); + + for _ in 0..ROUND_COUNT { + for monkey_idx in 0..monkey_state.len() { + while let Some(worry) = monkey_state[monkey_idx].items.pop() { + let worry = monkey_state[monkey_idx].operation.perform(worry); + + let throw_to = if worry % monkey_state[monkey_idx].test.divisible_by == 0 { + monkey_state[monkey_idx].test.if_true + } else { + monkey_state[monkey_idx].test.if_false + }; + + monkey_state[throw_to].items.push(worry % lcm); + + times_inspected[monkey_idx] += 1; + } + } + } + + times_inspected.sort_unstable(); + times_inspected.reverse(); + let result = times_inspected[0] * times_inspected[1]; + + println!("Part 2: {result}"); + + result +} + +#[cfg(test)] +mod tests { + const SAMPLE_INPUT: &str = include_str!("sample_input.txt"); + + #[test] + fn test_with_solution() { + assert_eq!( + super::part_2(&crate::parse_input(crate::INPUT)), + 14952185856 + ); + } + + #[test] + fn test_with_sample_solution() { + assert_eq!(super::part_2(&crate::parse_input(SAMPLE_INPUT)), 2713310158); + } +} diff --git a/rust/src/day11/sample_input.txt b/rust/src/day11/sample_input.txt new file mode 100644 index 0000000..c04eddb --- /dev/null +++ b/rust/src/day11/sample_input.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 \ No newline at end of file