Rust Day 11
This commit is contained in:
parent
57216e6c0b
commit
e7a06642aa
7 changed files with 337 additions and 2 deletions
2
rust/Cargo.lock
generated
2
rust/Cargo.lock
generated
|
@ -4,4 +4,4 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "advent-of-code"
|
name = "advent-of-code"
|
||||||
version = "22.8.2"
|
version = "22.11.2"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "advent-of-code"
|
name = "advent-of-code"
|
||||||
version = "22.8.2"
|
version = "22.11.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
@ -63,6 +63,10 @@ path = "src/day09/main.rs"
|
||||||
name = "day10"
|
name = "day10"
|
||||||
path = "src/day10/main.rs"
|
path = "src/day10/main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "day11"
|
||||||
|
path = "src/day11/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Required for one of the day06 solutions (commented out)
|
# Required for one of the day06 solutions (commented out)
|
||||||
# phf = { version = "0.11.1", features = ["macros"] }
|
# phf = { version = "0.11.1", features = ["macros"] }
|
55
rust/src/day11/input.txt
Normal file
55
rust/src/day11/input.txt
Normal 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
144
rust/src/day11/main.rs
Normal 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
48
rust/src/day11/part_1.rs
Normal 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
57
rust/src/day11/part_2.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
27
rust/src/day11/sample_input.txt
Normal file
27
rust/src/day11/sample_input.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
|
Loading…
Reference in a new issue