day11 finished
This commit is contained in:
parent
54fd03233a
commit
6509fc79ea
5 changed files with 334 additions and 2 deletions
|
|
@ -7,5 +7,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
itertools = "0.10"
|
||||
num-traits = "0.2.15"
|
||||
lazy_static = "1.4"
|
||||
num-traits = "0.2"
|
||||
regex = "1.7"
|
||||
thiserror = "1.0"
|
||||
|
|
|
|||
27
data/day11/example01.txt
Normal file
27
data/day11/example01.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
|
||||
55
data/day11/input.txt
Normal file
55
data/day11/input.txt
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
Monkey 0:
|
||||
Starting items: 97, 81, 57, 57, 91, 61
|
||||
Operation: new = old * 7
|
||||
Test: divisible by 11
|
||||
If true: throw to monkey 5
|
||||
If false: throw to monkey 6
|
||||
|
||||
Monkey 1:
|
||||
Starting items: 88, 62, 68, 90
|
||||
Operation: new = old * 17
|
||||
Test: divisible by 19
|
||||
If true: throw to monkey 4
|
||||
If false: throw to monkey 2
|
||||
|
||||
Monkey 2:
|
||||
Starting items: 74, 87
|
||||
Operation: new = old + 2
|
||||
Test: divisible by 5
|
||||
If true: throw to monkey 7
|
||||
If false: throw to monkey 4
|
||||
|
||||
Monkey 3:
|
||||
Starting items: 53, 81, 60, 87, 90, 99, 75
|
||||
Operation: new = old + 1
|
||||
Test: divisible by 2
|
||||
If true: throw to monkey 2
|
||||
If false: throw to monkey 1
|
||||
|
||||
Monkey 4:
|
||||
Starting items: 57
|
||||
Operation: new = old + 6
|
||||
Test: divisible by 13
|
||||
If true: throw to monkey 7
|
||||
If false: throw to monkey 0
|
||||
|
||||
Monkey 5:
|
||||
Starting items: 54, 84, 91, 55, 59, 72, 75, 70
|
||||
Operation: new = old * old
|
||||
Test: divisible by 7
|
||||
If true: throw to monkey 6
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 6:
|
||||
Starting items: 95, 79, 79, 68, 78
|
||||
Operation: new = old + 3
|
||||
Test: divisible by 3
|
||||
If true: throw to monkey 1
|
||||
If false: throw to monkey 3
|
||||
|
||||
Monkey 7:
|
||||
Starting items: 61, 97, 67
|
||||
Operation: new = old + 4
|
||||
Test: divisible by 17
|
||||
If true: throw to monkey 0
|
||||
If false: throw to monkey 5
|
||||
246
src/days/day11/mod.rs
Normal file
246
src/days/day11/mod.rs
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
use super::template::{DayTrait, ResultType};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use std::{iter::zip, num::ParseIntError};
|
||||
use thiserror::Error;
|
||||
|
||||
const DAY_NUMBER: usize = 11;
|
||||
|
||||
pub struct Day;
|
||||
|
||||
impl DayTrait for Day {
|
||||
fn get_day_number(&self) -> usize {
|
||||
DAY_NUMBER
|
||||
}
|
||||
|
||||
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let troop = Troop::parse(lines)?;
|
||||
Ok(ResultType::Integer(troop.play(20)))
|
||||
}
|
||||
|
||||
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||
let troop = Troop::parse(lines)?;
|
||||
Ok(ResultType::Integer(troop.play_again(10_000)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum MonkeyError {
|
||||
#[error("Not an Integer")]
|
||||
NotAnInteger(#[from] ParseIntError),
|
||||
|
||||
#[error("Can't parse line: {0}")]
|
||||
UnknownLine(String),
|
||||
|
||||
#[error("Did not expect end of Input")]
|
||||
PrematureEndOfInput,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Operation {
|
||||
Plus(i64),
|
||||
Times(i64),
|
||||
Squared,
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn parse(line: &str) -> Operation {
|
||||
match line.split_whitespace().collect::<Vec<_>>()[..] {
|
||||
["*", "old"] => Operation::Squared,
|
||||
["*", value] => Operation::Times(value.parse().unwrap()),
|
||||
["+", value] => Operation::Plus(value.parse().unwrap()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn calc(&self, old: i64) -> i64 {
|
||||
match self {
|
||||
Operation::Plus(value) => old + *value,
|
||||
Operation::Times(value) => old * value,
|
||||
Operation::Squared => old.pow(2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Monkey {
|
||||
number: usize,
|
||||
items: Vec<i64>,
|
||||
operation: Operation,
|
||||
divisor: i64,
|
||||
good_monkey: usize,
|
||||
bad_monkey: usize,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref MONKEY: Regex = Regex::new(r"Monkey (\d+)").unwrap();
|
||||
static ref STARTING: Regex = Regex::new(r"Starting items: (\d+(?:, \d+)*)").unwrap();
|
||||
static ref OP: Regex = Regex::new(r"Operation: new = old ([+*] \d+|\* old)").unwrap();
|
||||
static ref TEST: Regex = Regex::new(r"Test: divisible by (\d+)").unwrap();
|
||||
static ref NEXT: Regex = Regex::new(r"If (?:true|false): throw to monkey (\d+)").unwrap();
|
||||
}
|
||||
|
||||
impl Monkey {
|
||||
fn parse_line<'a>(re: &Regex, line: &'a str) -> Result<&'a str, MonkeyError> {
|
||||
let caps = re
|
||||
.captures(line)
|
||||
.ok_or(MonkeyError::UnknownLine(line.to_owned()))?;
|
||||
Ok(caps.get(1).unwrap().as_str())
|
||||
}
|
||||
|
||||
pub fn parse_one(
|
||||
iterator: &mut dyn Iterator<Item = &String>,
|
||||
) -> Result<Option<Monkey>, MonkeyError> {
|
||||
if let Some(line) = iterator.next() {
|
||||
let number: usize = Monkey::parse_line(&MONKEY, line)?.parse()?;
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let start = Monkey::parse_line(&STARTING, line)?;
|
||||
let items = start
|
||||
.split(", ")
|
||||
.map(|item| item.parse::<i64>())
|
||||
.collect::<Result<Vec<i64>, _>>()?;
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let operation = Operation::parse(Monkey::parse_line(&OP, line)?);
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let divisor: i64 = Monkey::parse_line(&TEST, line)?.parse()?;
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let good_monkey: usize = Monkey::parse_line(&NEXT, line)?.parse()?;
|
||||
|
||||
let line = iterator.next().ok_or(MonkeyError::PrematureEndOfInput)?;
|
||||
let bad_monkey: usize = Monkey::parse_line(&NEXT, line)?.parse()?;
|
||||
|
||||
iterator.next();
|
||||
|
||||
Ok(Some(Monkey {
|
||||
number,
|
||||
items,
|
||||
operation,
|
||||
divisor,
|
||||
good_monkey,
|
||||
bad_monkey,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(&self, value: i64) -> i64 {
|
||||
self.operation.calc(value)
|
||||
}
|
||||
|
||||
pub fn check(&self, value: i64) -> bool {
|
||||
value % self.divisor == 0
|
||||
}
|
||||
}
|
||||
|
||||
struct Troop {
|
||||
monkeys: Vec<Monkey>,
|
||||
}
|
||||
|
||||
impl Troop {
|
||||
pub fn parse(lines: &[String]) -> Result<Troop, MonkeyError> {
|
||||
let mut iter = lines.iter();
|
||||
let mut monkeys = Vec::new();
|
||||
while let Some(monkey) = Monkey::parse_one(&mut iter)? {
|
||||
monkeys.push(monkey);
|
||||
}
|
||||
Ok(Troop { monkeys })
|
||||
}
|
||||
|
||||
fn do_play<F>(&self, rounds: usize, alter: F) -> i64
|
||||
where
|
||||
F: Fn(i64) -> i64,
|
||||
{
|
||||
let mut holding = vec![Vec::new(); self.monkeys.len()];
|
||||
let mut inspected = vec![0i64; self.monkeys.len()];
|
||||
for (holding, monkey) in zip(&mut holding, &self.monkeys) {
|
||||
holding.extend(&monkey.items);
|
||||
}
|
||||
for _ in 0..rounds {
|
||||
for (pos, monkey) in self.monkeys.iter().enumerate() {
|
||||
let (good, bad): (Vec<_>, Vec<_>) = holding[pos]
|
||||
.iter()
|
||||
.map(|value| alter(monkey.process(*value)))
|
||||
.partition(|value| monkey.check(*value));
|
||||
inspected[pos] += holding[pos].len() as i64;
|
||||
holding.get_mut(pos).unwrap().clear();
|
||||
holding.get_mut(monkey.good_monkey).unwrap().extend(&good);
|
||||
holding.get_mut(monkey.bad_monkey).unwrap().extend(&bad);
|
||||
}
|
||||
}
|
||||
|
||||
inspected.sort_by(|a, b| b.cmp(a));
|
||||
inspected.iter().take(2).product()
|
||||
}
|
||||
|
||||
pub fn play(&self, rounds: usize) -> i64 {
|
||||
self.do_play(rounds, |v| v / 3)
|
||||
}
|
||||
|
||||
pub fn play_again(&self, rounds: usize) -> i64 {
|
||||
let divisor: i64 = self.monkeys.iter().map(|monkey| monkey.divisor).product();
|
||||
self.do_play(rounds, |v| v % divisor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::common::file::read_lines;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_parse() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = Monkey {
|
||||
number: 0,
|
||||
items: vec![79, 98],
|
||||
operation: Operation::Times(19),
|
||||
divisor: 23,
|
||||
good_monkey: 2,
|
||||
bad_monkey: 3,
|
||||
};
|
||||
let result = Monkey::parse_one(&mut lines.iter())?.unwrap();
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_all() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = 4;
|
||||
let result = Troop::parse(&lines)?;
|
||||
assert_eq!(result.monkeys.len(), expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(10605);
|
||||
let result = day.part1(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() -> Result<()> {
|
||||
let day = Day {};
|
||||
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||
let expected = ResultType::Integer(2713310158);
|
||||
let result = day.part2(&lines)?;
|
||||
assert_eq!(result, expected);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ mod day07;
|
|||
mod day08;
|
||||
mod day09;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod template;
|
||||
|
||||
pub use template::DayTrait;
|
||||
|
|
@ -17,7 +18,7 @@ pub mod day_provider {
|
|||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
const MAX_DAY: usize = 10;
|
||||
const MAX_DAY: usize = 11;
|
||||
|
||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||
match day_num {
|
||||
|
|
@ -31,6 +32,7 @@ pub mod day_provider {
|
|||
8 => Ok(Box::new(day08::Day)),
|
||||
9 => Ok(Box::new(day09::Day)),
|
||||
10 => Ok(Box::new(day10::Day)),
|
||||
11 => Ok(Box::new(day11::Day)),
|
||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue