Rust Day 11

This commit is contained in:
Tobias Berger 2022-12-12 16:15:14 +01:00
parent 57216e6c0b
commit e7a06642aa
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
7 changed files with 337 additions and 2 deletions

2
rust/Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]]
name = "advent-of-code"
version = "22.8.2"
version = "22.11.2"

View file

@ -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"] }

55
rust/src/day11/input.txt Normal file
View file

@ -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

144
rust/src/day11/main.rs Normal file
View file

@ -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<u64>,
pub operation: Op,
pub test: Test,
}
pub(crate) fn parse_input(input: &'static str) -> Vec<Monkey> {
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::<String>();
for _ in 0.."\n Starting items: ".len() {
chars.next();
}
let starting_items_string = chars
.by_ref()
.take_while(|char| !matches!(char, '\n'))
.collect::<String>();
for _ in 0.." Operation: new = old ".len() {
chars.next();
}
let operation_string = chars
.by_ref()
.take_while(|char| !matches!(char, '\n'))
.collect::<String>();
for _ in 0.." Test: divisible by ".len() {
chars.next();
}
let divisible_by_string = chars
.by_ref()
.take_while(|char| matches!(char, '-' | '0'..='9'))
.collect::<String>();
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::<String>();
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::<String>();
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::<String>()
.parse()
.unwrap(),
)
} else if operation_string.chars().nth(2) == Some('o') {
Op::Square
} else {
Op::Mul(
operation_string
.chars()
.skip(2)
.collect::<String>()
.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);
}

48
rust/src/day11/part_1.rs Normal file
View file

@ -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);
}
}

57
rust/src/day11/part_2.rs Normal file
View file

@ -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::<u64>();
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);
}
}

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