diff --git a/2022/src/common.rs b/2022/src/common.rs index 5ec5bc8..3edc925 100644 --- a/2022/src/common.rs +++ b/2022/src/common.rs @@ -1,5 +1,7 @@ //! Common helper utilities to all days +use std::cmp::Ordering; + use anyhow::Result; use nom::combinator::map; use nom::error::ErrorKind; @@ -116,3 +118,18 @@ where (b, a) } } + +/// Some magic to get two mutable references into the same slice +pub fn get_both(slice: &mut [T], first: usize, second: usize) -> (&mut T, &mut T) { + match first.cmp(&second) { + Ordering::Greater => { + let (begin, end) = slice.split_at_mut(first); + (&mut end[0], &mut begin[second]) + } + Ordering::Less => { + let (begin, end) = slice.split_at_mut(second); + (&mut begin[first], &mut end[0]) + } + Ordering::Equal => panic!("Tried to get the same index twice {first}"), + } +} diff --git a/2022/src/day05.rs b/2022/src/day05.rs index e5c17fb..ef0e392 100644 --- a/2022/src/day05.rs +++ b/2022/src/day05.rs @@ -1,5 +1,3 @@ -use std::cmp::Ordering; - use anyhow::Result; use nom::branch::alt; use nom::bytes::complete::tag; @@ -17,6 +15,7 @@ use nom::sequence::tuple; use nom::IResult; use crate::common::enumerate; +use crate::common::get_both; use crate::common::parse_input; type Move = (usize, usize, usize); @@ -91,21 +90,6 @@ fn parse_task(input: &[u8]) -> IResult<&[u8], (OwnedStacks, Vec)> { Ok((input, (stacks, moves))) } -/// Some magic to get two mutable references into the same slice -fn get_both(stacks: &mut [Vec], from: usize, to: usize) -> (&mut Vec, &mut Vec) { - match from.cmp(&to) { - Ordering::Greater => { - let (begin, end) = stacks.split_at_mut(from); - (&mut end[0], &mut begin[to]) - } - Ordering::Less => { - let (begin, end) = stacks.split_at_mut(to); - (&mut begin[from], &mut end[0]) - } - Ordering::Equal => panic!("Tried to stack from and to {from}"), - } -} - fn compute_answer(stacks: &mut [Vec]) -> Result { let mut result = String::with_capacity(stacks.len()); diff --git a/2022/src/day11.rs b/2022/src/day11.rs index acd2238..b18a9d3 100644 --- a/2022/src/day11.rs +++ b/2022/src/day11.rs @@ -1,9 +1,146 @@ use anyhow::Result; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::bytes::complete::take; +use nom::character::complete::digit1; +use nom::character::complete::newline; +use nom::combinator::map; +use nom::combinator::map_res; +use nom::multi::separated_list0; +use nom::multi::separated_list1; +use nom::sequence::delimited; +use nom::sequence::preceded; +use nom::sequence::separated_pair; +use nom::sequence::tuple; +use nom::IResult; -pub fn part1(_input: &[u8]) -> Result { - anyhow::bail!("not implemented") +use crate::common::parse_input; + +#[derive(Debug, Copy, Clone)] +enum Operation { + Mul(u32), + Add(u32), + Square, +} + +impl Operation { + fn transform(self, worry: u32) -> u32 { + match self { + Operation::Mul(val) => worry * val, + Operation::Add(val) => worry + val, + Operation::Square => worry * worry, + } + } +} + +#[derive(Debug)] +struct Monkey { + items: Vec, + operation: Operation, + test_mod: u32, + targets: [usize; 2], + inspected: usize, +} + +impl Monkey { + fn handle_items(&mut self, drains: &mut [Vec; 2]) { + self.inspected += self.items.len(); + + for item in self.items.drain(..) { + let mut new_val = self.operation.transform(item); + // Miraculously get less worried + new_val /= 3; + + drains[(new_val % self.test_mod == 0) as usize].push(new_val); + } + } +} + +fn parse_operation(input: &[u8]) -> IResult<&[u8], Operation> { + preceded( + tag("new = old "), + alt(( + map_res( + separated_pair(take(1usize), tag(" "), nom::character::complete::u32), + |(op, val): (&[u8], u32)| match op[0] { + b'*' => Ok(Operation::Mul(val)), + b'+' => Ok(Operation::Add(val)), + _ => Err(anyhow::anyhow!("Invalid operation {op:?}")), + }, + ), + map(tag("* old"), |_| Operation::Square), + )), + )(input) +} + +fn parse_monkey(input: &[u8]) -> IResult<&[u8], Monkey> { + use nom::character::complete::u32; + + map( + preceded( + // Skip the useless header line + tuple((tag("Monkey "), digit1, tag(":\n"))), + // Parse the actual interesting bits of the monkey + tuple(( + // Parse the starting items + delimited( + tag(" Starting items: "), + separated_list1(tag(", "), u32), + newline, + ), + // Parse operation + delimited(tag(" Operation: "), parse_operation, newline), + // Parse the test + delimited(tag(" Test: divisible by "), u32, newline), + // Parse both cases + delimited(tag(" If true: throw to monkey "), u32, newline), + delimited(tag(" If false: throw to monkey "), u32, newline), + )), + ), + |(items, operation, test_mod, if_true, if_false)| Monkey { + items, + operation, + test_mod, + targets: [if_false as usize, if_true as usize], + inspected: 0, + }, + )(input) +} + +pub fn part1(input: &[u8]) -> Result { + let mut monkeys = parse_input(input, separated_list0(newline, parse_monkey))?; + let mut drains = [Vec::new(), Vec::new()]; + + for _ in 0..20 { + for i in 0..monkeys.len() { + monkeys[i].handle_items(&mut drains); + + for j in 0..2 { + let target = monkeys[i].targets[j]; + monkeys[target].items.append(&mut drains[j]); + } + } + } + + monkeys.sort_by(|a, b| b.inspected.cmp(&a.inspected)); + + let result: usize = monkeys[0].inspected * monkeys[1].inspected; + + Ok(result.to_string()) } pub fn part2(_input: &[u8]) -> Result { anyhow::bail!("not implemented") } + +#[cfg(test)] +mod tests { + use super::*; + + const SAMPLE: &[u8] = include_bytes!("samples/11.txt"); + + #[test] + fn sample_part1() { + assert_eq!(part1(SAMPLE).unwrap(), "10605"); + } +}