Day 11
This commit is contained in:
parent
8ec6b0413b
commit
8014d9d966
7
day11/Cargo.lock
generated
Normal file
7
day11/Cargo.lock
generated
Normal 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
55
day11/input.txt
Normal 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
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user