day11 finished

This commit is contained in:
Ruediger Ludwig 2023-02-07 21:16:55 +01:00
parent 54fd03233a
commit 6509fc79ea
5 changed files with 334 additions and 2 deletions

246
src/days/day11/mod.rs Normal file
View 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(())
}
}

View file

@ -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)),
}
}