day 11
This commit is contained in:
83
Cargo.lock
generated
83
Cargo.lock
generated
@@ -8,8 +8,15 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"nom",
|
||||
"num",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
@@ -46,3 +53,79 @@ dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
@@ -8,3 +8,4 @@ edition = "2021"
|
||||
[dependencies]
|
||||
itertools = "0.10.5"
|
||||
nom = "7.1.1"
|
||||
num = "0.4.0"
|
||||
|
||||
55
inputs/day11.txt
Normal file
55
inputs/day11.txt
Normal file
@@ -0,0 +1,55 @@
|
||||
Monkey 0:
|
||||
Starting items: 56, 52, 58, 96, 70, 75, 72
|
||||
Operation: new = old * 17
|
||||
Test: divisible by 11
|
||||
If true: throw to monkey 2
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 1:
|
||||
Starting items: 75, 58, 86, 80, 55, 81
|
||||
Operation: new = old + 7
|
||||
Test: divisible by 3
|
||||
If true: throw to monkey 6
|
||||
If false: throw to monkey 5
|
||||
|
||||
Monkey 2:
|
||||
Starting items: 73, 68, 73, 90
|
||||
Operation: new = old * old
|
||||
Test: divisible by 5
|
||||
If true: throw to monkey 1
|
||||
If false: throw to monkey 7
|
||||
|
||||
Monkey 3:
|
||||
Starting items: 72, 89, 55, 51, 59
|
||||
Operation: new = old + 1
|
||||
Test: divisible by 7
|
||||
If true: throw to monkey 2
|
||||
If false: throw to monkey 7
|
||||
|
||||
Monkey 4:
|
||||
Starting items: 76, 76, 91
|
||||
Operation: new = old * 3
|
||||
Test: divisible by 19
|
||||
If true: throw to monkey 0
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 5:
|
||||
Starting items: 88
|
||||
Operation: new = old + 4
|
||||
Test: divisible by 2
|
||||
If true: throw to monkey 6
|
||||
If false: throw to monkey 4
|
||||
|
||||
Monkey 6:
|
||||
Starting items: 64, 63, 56, 50, 77, 55, 55, 86
|
||||
Operation: new = old + 8
|
||||
Test: divisible by 13
|
||||
If true: throw to monkey 4
|
||||
If false: throw to monkey 0
|
||||
|
||||
Monkey 7:
|
||||
Starting items: 79, 58
|
||||
Operation: new = old + 6
|
||||
Test: divisible by 17
|
||||
If true: throw to monkey 1
|
||||
If false: throw to monkey 5
|
||||
8
src/bin/day11_1.rs
Normal file
8
src/bin/day11_1.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use std::fs;
|
||||
|
||||
use aoc2022::day11::process_part_1;
|
||||
|
||||
fn main() {
|
||||
let file = fs::read_to_string("./inputs/day11.txt").unwrap();
|
||||
println!("{}", process_part_1(&file));
|
||||
}
|
||||
8
src/bin/day11_2.rs
Normal file
8
src/bin/day11_2.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use std::fs;
|
||||
|
||||
use aoc2022::day11::process_part_2;
|
||||
|
||||
fn main() {
|
||||
let file = fs::read_to_string("./inputs/day11.txt").unwrap();
|
||||
println!("{}", process_part_2(&file));
|
||||
}
|
||||
204
src/day11.rs
Normal file
204
src/day11.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use itertools::Itertools;
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete;
|
||||
use nom::character::complete::{newline, space1};
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::{delimited, preceded};
|
||||
use nom::{IResult, Parser};
|
||||
|
||||
pub fn process_part_1(input: &str) -> usize {
|
||||
let mut monkeys = parse(input).unwrap().1;
|
||||
|
||||
// Sadly we are doing concurrent array indexing in rust with borrows
|
||||
for _round in 0..20 {
|
||||
for monkey_id in 0..monkeys.len() {
|
||||
while !monkeys[monkey_id].items.is_empty() {
|
||||
// I'm relying on rust's magic 'figure out when we can drop this to make it work'
|
||||
// here. This var can be dropped *just* before the last line of the block to make it
|
||||
// so we aren't borrowing mutably twice.
|
||||
let monkey = &mut monkeys[monkey_id];
|
||||
|
||||
monkey.num_inspected += 1;
|
||||
let item = monkey.items.pop_front().unwrap();
|
||||
let item = monkey.operation.execute(item) / 3;
|
||||
let next_monkey_id = if item % monkey.test_mod == 0 {
|
||||
monkey.target_true
|
||||
} else {
|
||||
monkey.target_false
|
||||
};
|
||||
monkeys[next_monkey_id].items.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monkeys
|
||||
.iter()
|
||||
.map(|monkey| monkey.num_inspected)
|
||||
.sorted()
|
||||
.rev()
|
||||
.take(2)
|
||||
.product()
|
||||
}
|
||||
|
||||
pub fn process_part_2(input: &str) -> usize {
|
||||
let mut monkeys = parse(input).unwrap().1;
|
||||
|
||||
// Since we only care for divisibility in tests, we can just modulo every item with
|
||||
// the LCM of all tests. This allows all tests to check for eligibility without actually
|
||||
// letting the number grow huge.
|
||||
let lcm = monkeys
|
||||
.iter()
|
||||
.map(|x| x.test_mod)
|
||||
.reduce(num::integer::lcm)
|
||||
.unwrap();
|
||||
|
||||
for _round in 0..10000 {
|
||||
for monkey_id in 0..monkeys.len() {
|
||||
while !monkeys[monkey_id].items.is_empty() {
|
||||
let monkey = &mut monkeys[monkey_id];
|
||||
monkey.num_inspected += 1;
|
||||
|
||||
let item = monkey.items.pop_front().unwrap();
|
||||
let item = monkey.operation.execute(item) % lcm;
|
||||
let next_monkey_id = if item % monkey.test_mod == 0 {
|
||||
monkey.target_true
|
||||
} else {
|
||||
monkey.target_false
|
||||
};
|
||||
monkeys[next_monkey_id].items.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monkeys
|
||||
.iter()
|
||||
.map(|monkey| monkey.num_inspected)
|
||||
.sorted()
|
||||
.rev()
|
||||
.take(2)
|
||||
.product()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Operation {
|
||||
Add(u64),
|
||||
Multiply(u64),
|
||||
MultiplySelf,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
fn execute(&self, item: u64) -> u64 {
|
||||
match self {
|
||||
Operation::Add(x) => item + x,
|
||||
Operation::Multiply(x) => item * x,
|
||||
Operation::MultiplySelf => item * item,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Monkey {
|
||||
items: VecDeque<u64>,
|
||||
operation: Operation,
|
||||
test_mod: u64,
|
||||
target_true: usize,
|
||||
target_false: usize,
|
||||
num_inspected: usize,
|
||||
}
|
||||
|
||||
fn parse(input: &str) -> IResult<&str, Vec<Monkey>> {
|
||||
separated_list1(newline, parse_monkey)(input)
|
||||
}
|
||||
|
||||
fn parse_monkey(input: &str) -> IResult<&str, Monkey> {
|
||||
let (input, _id) = delimited(tag("Monkey "), complete::u64, tag(":\n"))(input)?;
|
||||
let (input, items) = preceded(
|
||||
space1,
|
||||
delimited(
|
||||
tag("Starting items: "),
|
||||
separated_list1(tag(", "), complete::u64),
|
||||
newline,
|
||||
),
|
||||
)(input)?;
|
||||
let (input, operation) = delimited(space1, parse_operation, newline)(input)?;
|
||||
let (input, test_mod) = preceded(
|
||||
space1,
|
||||
delimited(tag("Test: divisible by "), complete::u64, newline),
|
||||
)(input)?;
|
||||
let (input, target_true) = preceded(
|
||||
space1,
|
||||
delimited(tag("If true: throw to monkey "), complete::u64, newline),
|
||||
)(input)?;
|
||||
let (input, target_false) = preceded(
|
||||
space1,
|
||||
delimited(tag("If false: throw to monkey "), complete::u64, newline),
|
||||
)(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Monkey {
|
||||
items: VecDeque::from(items),
|
||||
operation,
|
||||
test_mod,
|
||||
target_true: target_true as usize,
|
||||
target_false: target_false as usize,
|
||||
num_inspected: 0,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_operation(input: &str) -> IResult<&str, Operation> {
|
||||
let (input, _) = tag("Operation: new = old ")(input)?;
|
||||
alt((
|
||||
tag("* old").map(|_| Operation::MultiplySelf),
|
||||
preceded(tag("+ "), complete::u64).map(Operation::Add),
|
||||
preceded(tag("* "), complete::u64).map(Operation::Multiply),
|
||||
))(input)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const INPUT: &str = "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
|
||||
";
|
||||
|
||||
#[test]
|
||||
fn day1() {
|
||||
assert_eq!(process_part_1(INPUT), 10605);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day2() {
|
||||
assert_eq!(process_part_2(INPUT), 2713310158);
|
||||
}
|
||||
}
|
||||
@@ -11,3 +11,4 @@ pub mod day07;
|
||||
pub mod day08;
|
||||
pub mod day09;
|
||||
pub mod day10;
|
||||
pub mod day11;
|
||||
|
||||
Reference in New Issue
Block a user