This commit is contained in:
Max Känner 2022-12-11 18:59:34 +01:00
parent 8ec6b0413b
commit 8014d9d966
3 changed files with 487 additions and 2 deletions

7
day11/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "day11"
version = "0.1.0"

55
day11/input.txt Normal file
View File

@ -0,0 +1,55 @@
Monkey 0:
Starting items: 84, 66, 62, 69, 88, 91, 91
Operation: new = old * 11
Test: divisible by 2
If true: throw to monkey 4
If false: throw to monkey 7
Monkey 1:
Starting items: 98, 50, 76, 99
Operation: new = old * old
Test: divisible by 7
If true: throw to monkey 3
If false: throw to monkey 6
Monkey 2:
Starting items: 72, 56, 94
Operation: new = old + 1
Test: divisible by 13
If true: throw to monkey 4
If false: throw to monkey 0
Monkey 3:
Starting items: 55, 88, 90, 77, 60, 67
Operation: new = old + 2
Test: divisible by 3
If true: throw to monkey 6
If false: throw to monkey 5
Monkey 4:
Starting items: 69, 72, 63, 60, 72, 52, 63, 78
Operation: new = old * 13
Test: divisible by 19
If true: throw to monkey 1
If false: throw to monkey 7
Monkey 5:
Starting items: 89, 73
Operation: new = old + 5
Test: divisible by 17
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 6:
Starting items: 78, 68, 98, 88, 66
Operation: new = old + 6
Test: divisible by 11
If true: throw to monkey 2
If false: throw to monkey 5
Monkey 7:
Starting items: 70
Operation: new = old + 7
Test: divisible by 5
If true: throw to monkey 1
If false: throw to monkey 3

View File

@ -1,3 +1,426 @@
fn main() {
println!("Hello, world!");
use std::{
cmp::{Ordering, Reverse},
fs::read_to_string,
io::Write,
ops::{Add, Div, Mul, Rem, Shl, Sub},
rc::Rc,
str::Lines,
};
#[derive(Debug, Clone)]
struct BigInt {
numbers: Vec<u64>,
}
impl From<i32> for BigInt {
fn from(int: i32) -> Self {
Self {
numbers: vec![int as u64],
}
}
}
impl From<u64> for BigInt {
fn from(int: u64) -> Self {
Self { numbers: vec![int] }
}
}
impl From<u128> for BigInt {
fn from(int: u128) -> Self {
let lower = ((int << 64) >> 64) as u64;
let higher = (int >> 64) as u64;
Self {
numbers: vec![lower, higher],
}
.shrink()
}
}
impl Add<BigInt> for BigInt {
type Output = BigInt;
fn add(self, rhs: BigInt) -> Self::Output {
let (mut output, other) = if self.numbers.len() > rhs.numbers.len() {
(self, rhs)
} else {
(rhs, self)
};
let mut carry = false;
let mut shortest_length = other.numbers.len();
for i in 0..shortest_length {
let new_carry;
(output.numbers[i], new_carry) = output.numbers[i].overflowing_add(other.numbers[i]);
if carry {
(output.numbers[i], carry) = output.numbers[i].overflowing_add(1);
}
carry = new_carry || carry;
}
while carry {
if output.numbers.len() == shortest_length {
output.numbers.push(0);
}
(output.numbers[shortest_length], carry) =
output.numbers[shortest_length].overflowing_add(1);
shortest_length += 1;
}
output.shrink()
}
}
impl Sub for BigInt {
type Output = BigInt;
fn sub(self, other: Self) -> Self::Output {
let mut s = self;
if s < other {
panic!("cannot subtract bigger number from smaller number");
}
let mut carry = false;
for i in (0..other.numbers.len()).rev() {
let new_carry;
(s.numbers[i], new_carry) = s.numbers[i].overflowing_sub(other.numbers[i]);
if carry {
(s.numbers[i], carry) = s.numbers[i].overflowing_sub(1);
}
carry = new_carry || carry;
}
s.shrink()
}
}
impl Mul for BigInt {
type Output = BigInt;
fn mul(self, other: Self) -> Self::Output {
let mut output: Self = 0.into();
for &digit in other.numbers.iter().rev() {
output = (output << 64) + (digit * self.clone());
}
output.shrink()
}
}
impl Mul<BigInt> for u64 {
type Output = BigInt;
fn mul(self, rhs: BigInt) -> Self::Output {
let mut output: BigInt = 0.into();
for &digit in rhs.numbers.iter().rev() {
output = (output << 64) + (digit as u128 * self as u128).into();
}
output.shrink()
}
}
impl BigInt {
fn div_rem(self, other: Self) -> (Self, Self) {
let mut q: Self = Self {
numbers: vec![0; self.numbers.len()],
};
let mut r: Self = 0.into();
for i in (0..(self.numbers.len() * 64)).rev() {
r = r << 1;
let bit_index = i / 64;
let bit = i % 64;
r.numbers[0] |= u64::from(self.numbers[bit_index] & (1 << bit) != 0);
if r >= other.clone() {
r = r - other.clone();
q.numbers[bit_index] |= 1 << bit;
}
}
(q.shrink(), r.shrink())
}
fn shrink(self) -> Self {
let mut s = self;
for i in (1..s.numbers.len()).rev() {
if s.numbers[i] == 0 {
s.numbers.pop();
} else {
break;
}
}
s
}
}
impl Div for BigInt {
type Output = BigInt;
fn div(self, other: Self) -> Self::Output {
let (div, _) = self.div_rem(other);
div
}
}
impl Rem for BigInt {
type Output = BigInt;
fn rem(self, other: Self) -> Self::Output {
let (_, rem) = self.div_rem(other);
rem
}
}
impl Shl<usize> for BigInt {
type Output = BigInt;
fn shl(self, rhs: usize) -> Self::Output {
let mut output = self.clone();
let q = rhs / 64;
let r = rhs % 64;
output.numbers.push(0);
for i in (1..self.numbers.len()).rev() {
output.numbers[i] <<= r;
output.numbers[i] |= output.numbers[i] >> (64 - 4);
}
output.numbers[0] <<= r;
for _ in 0..q {
output.numbers.push(0);
for i in (1..output.numbers.len()).rev() {
output.numbers[i] = output.numbers[i - 1];
}
output.numbers[0] = 0;
}
output.shrink()
}
}
impl PartialOrd for BigInt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let biggest_length = self.numbers.len().max(other.numbers.len());
for i in (0..biggest_length).rev() {
if i >= self.numbers.len() && other.numbers[i] != 0 {
return Some(Ordering::Less);
} else if i >= other.numbers.len() && self.numbers[i] != 0 {
return Some(Ordering::Greater);
} else if i >= self.numbers.len() || i >= other.numbers.len() {
continue;
} else if self.numbers[i] < other.numbers[i] {
return Some(Ordering::Less);
} else if self.numbers[i] > other.numbers[i] {
return Some(Ordering::Greater);
}
}
Some(Ordering::Equal)
}
}
impl PartialEq for BigInt {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
type Item = BigInt;
#[derive(Clone)]
struct Monkey {
index: usize,
items: Vec<Item>,
inspected_items: usize,
operation: Rc<dyn Fn(Item) -> Item>,
test_divisible_by: Item,
test_true: usize,
test_false: usize,
}
impl std::fmt::Debug for Monkey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Monkey")
.field("index", &self.index)
.field("items", &self.items)
.field("inspected_items", &self.inspected_items)
.field("test_divisible_by", &self.test_divisible_by)
.field("test_true", &self.test_true)
.field("test_false", &self.test_false)
.finish()
}
}
impl Monkey {
fn parse(input: &mut Lines) -> Option<Self> {
let mut input = input
.take_while(|line| !line.is_empty())
.map(|line| line.split_whitespace().collect::<Vec<_>>());
let index = match &input.next()?[..] {
["Monkey", i] => i.replace(':', "").parse().unwrap(),
l => panic!("invalid line: {l:?}"),
};
let line = input.next()?;
let items = match &line[..2] {
["Starting", "items:"] => line[2..]
.iter()
.map(|n| n.replace(',', "").parse::<u64>().unwrap().into())
.collect::<Vec<_>>(),
l => panic!("invalid line: {l:?}"),
};
let operation: Rc<dyn Fn(Item) -> Item> = match &input.next()?[..] {
["Operation:", "new", "=", "old", "+", "old"] => Rc::new(|old| old.clone() + old),
["Operation:", "new", "=", "old", "+", n] => {
let n: Item = n.parse::<u64>().unwrap().into();
Rc::new(move |old| old + n.clone())
}
["Operation:", "new", "=", "old", "*", "old"] => Rc::new(|old| old.clone() * old),
["Operation:", "new", "=", "old", "*", n] => {
let n: Item = n.parse::<u64>().unwrap().into();
Rc::new(move |old| old * n.clone())
}
l => panic!("invalid line: {l:?}"),
};
let test_divisible_by = match &input.next()?[..] {
["Test:", "divisible", "by", n] => n.parse::<u64>().unwrap().into(),
l => panic!("invalid line: {l:?}"),
};
let test_true = match &input.next()?[..] {
["If", "true:", "throw", "to", "monkey", n] => n.parse().unwrap(),
l => panic!("invalid line: {l:?}"),
};
let test_false = match &input.next()?[..] {
["If", "false:", "throw", "to", "monkey", n] => n.parse().unwrap(),
l => panic!("invalid line: {l:?}"),
};
Some(Self {
index,
items,
operation,
test_divisible_by,
test_true,
test_false,
inspected_items: 0,
})
}
fn execute_round(
&mut self,
relief: impl Fn(Item) -> Item,
) -> (usize, Vec<Item>, usize, Vec<Item>) {
let (items_true, items_false) = self
.items
.iter()
.map(|item| (self.operation)(item.clone()))
.map(relief)
.partition(|item| item.clone() % self.test_divisible_by.clone() == 0u64.into());
self.inspected_items += self.items.len();
self.items = vec![];
(self.test_true, items_true, self.test_false, items_false)
}
}
fn execute_round(monkeys: &mut [Monkey], relief: impl Fn(Item) -> Item) {
for index in 0..monkeys.len() {
let (index_true, mut items_true, index_false, mut items_false) =
monkeys[index].execute_round(&relief);
monkeys[index_true].items.append(&mut items_true);
monkeys[index_false].items.append(&mut items_false);
}
}
fn calc_monkey_business(monkeys: &[Monkey]) -> usize {
let mut inspected_items = monkeys
.iter()
.map(|monkey| monkey.inspected_items)
.collect::<Vec<_>>();
inspected_items.sort_by_key(|&items| Reverse(items));
inspected_items.iter().take(2).product()
}
fn main() {
let input = "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";
let input = read_to_string("input.txt").unwrap_or_else(|_| input.to_owned());
let mut lines = input.lines();
let mut monkeys = vec![];
while let Some(monkey) = Monkey::parse(&mut lines) {
lines.next();
monkeys.push(monkey);
}
let min_product: BigInt = monkeys
.iter()
.map(|monkey| monkey.test_divisible_by.clone())
.fold(1.into(), |p, i| p * i);
let mut monkeys2 = monkeys.clone();
for _ in 0..20 {
execute_round(&mut monkeys, |item| item / Item::from(3u64));
}
let monkey_business = calc_monkey_business(&monkeys);
println!("monkey business: {monkey_business}");
println!("starting 10000 iterations");
for i in 0..10_000 {
print!("executing round {i}\r");
std::io::stdout().flush().unwrap();
execute_round(&mut monkeys2, |item| item % min_product.clone());
}
let monkey_business = calc_monkey_business(&monkeys2);
println!("monkey business: {monkey_business}");
}
#[cfg(test)]
mod tests {
use super::BigInt;
#[test]
fn big_int() {
let x: BigInt = 1.into();
let y: BigInt = 1.into();
assert_eq!(
x.clone() << 64,
BigInt {
numbers: vec![0, 1]
}
);
assert_eq!(
x.clone() << 2,
BigInt {
numbers: vec![0b100, 0]
}
);
assert_eq!(
x.clone() << 65,
BigInt {
numbers: vec![0, 2, 0]
}
);
assert_eq!(x.clone() + y.clone(), 2.into());
assert_eq!(x.clone() - y.clone(), 0.into());
assert_eq!(x.clone() * y.clone(), 1.into());
assert_eq!(x.clone() / y.clone(), 1.into());
assert_eq!(x % y, 0.into());
let x: BigInt = 101.into();
let y: BigInt = 10.into();
assert_eq!(x.clone() + y.clone(), 111.into());
assert_eq!(x.clone() - y.clone(), 91.into());
assert_eq!(x.clone() * y.clone(), 1010.into());
assert_eq!(x.clone() / y.clone(), 10.into());
assert_eq!(x % y, 1.into());
}
}