From c49814908e39d647fcf3438deb15acc230ae993e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Christian=20Gr=C3=BCnhage?= Date: Sat, 18 Dec 2021 17:28:33 +0100 Subject: [PATCH] implement day 18 --- src/day_18/mod.rs | 68 +++++++++++ src/day_18/model.rs | 258 ++++++++++++++++++++++++++++++++++++++++++ src/day_18/parsing.rs | 68 +++++++++++ src/lib.rs | 1 + 4 files changed, 395 insertions(+) create mode 100644 src/day_18/mod.rs create mode 100644 src/day_18/model.rs create mode 100644 src/day_18/parsing.rs diff --git a/src/day_18/mod.rs b/src/day_18/mod.rs new file mode 100644 index 0000000..5812cde --- /dev/null +++ b/src/day_18/mod.rs @@ -0,0 +1,68 @@ +mod model; +mod parsing; + +use aoc_runner_derive::{aoc, aoc_generator}; +use itertools::Itertools; +pub use model::SnailfishNumber; +pub use parsing::parse_number_list; +use yap::IntoTokens; + +#[aoc_generator(day18)] +pub fn parse_input(input: &str) -> Vec { + parse_number_list(&mut input.into_tokens()).unwrap() +} + +#[aoc(day18, part1)] +pub fn part1(input: &[SnailfishNumber]) -> isize { + let mut iter = input.iter(); + let mut accu = iter.next().unwrap().clone(); + for number in iter { + accu = accu + number.clone(); + } + accu.magnitude() +} + +#[aoc(day18, part2)] +pub fn part2(input: &[SnailfishNumber]) -> isize { + input + .iter() + .cloned() + .cartesian_product(input.iter().cloned()) + .filter(|(a, b)| a != b) + .map(|(a, b)| (a + b).magnitude()) + .max() + .unwrap() +} + +#[cfg(test)] +mod test { + const EXAMPLE_INPUT: &str = "[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]] +[[[5,[2,8]],4],[5,[[9,9],0]]] +[6,[[[6,2],[5,6]],[[7,6],[4,7]]]] +[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]] +[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]] +[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]] +[[[[5,4],[7,7]],8],[[8,3],8]] +[[9,3],[[9,9],[6,[4,9]]]] +[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]] +[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]]"; + const RESULT_PART_1: isize = 4140; + const RESULT_PART_2: isize = 3993; + + #[test] + fn part1_example() { + let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_1); + } + + #[test] + fn part2_example() { + let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_2); + } + + #[test] + fn parse_example() { + super::parse_input(EXAMPLE_INPUT); + } +} diff --git a/src/day_18/model.rs b/src/day_18/model.rs new file mode 100644 index 0000000..3a79f57 --- /dev/null +++ b/src/day_18/model.rs @@ -0,0 +1,258 @@ +use std::fmt::Debug; +use std::ops::Deref; +use std::{borrow::Borrow, ops::Add}; + +#[derive(Clone, PartialEq)] +pub enum SnailfishNumber { + Pair(Box, Box), + Regular(isize), +} + +impl Debug for SnailfishNumber { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SnailfishNumber::Pair(lhs, rhs) => { + f.write_str("[")?; + Debug::fmt(&lhs, f)?; + f.write_str(",")?; + Debug::fmt(&rhs, f)?; + f.write_str("]")?; + Ok(()) + } + SnailfishNumber::Regular(n) => Debug::fmt(n, f), + } + } +} + +impl From for SnailfishNumber +where + T: Borrow>, +{ + fn from(s: T) -> Self { + s.borrow().deref().clone() + } +} + +impl From for SnailfishNumber { + fn from(s: isize) -> Self { + Self::Regular(s) + } +} + +impl From<&isize> for SnailfishNumber { + fn from(s: &isize) -> Self { + Self::Regular(*s) + } +} + +impl From<(LHS, RHS)> for SnailfishNumber +where + LHS: Into, + RHS: Into, +{ + fn from(s: (LHS, RHS)) -> Self { + Self::Pair(Box::new(s.0.into()), Box::new(s.1.into())) + } +} + +//impl From<&SnailfishNumber> for SnailfishNumberIterator { +// fn from(inner: &SnailfishNumber) -> Self { +// SnailfishNumberIterator { inner: inner.clone(), index: 0 } +// } +//} +// +//pub struct SnailfishNumberIterator { +// inner: SnailfishNumber, +// index: usize, +//} +// +//impl Iterator for SnailfishNumberIterator { +// type Item = isize; +// +// fn next(&mut self) -> Option { +// let next = match self.inner { +// SnailfishNumber::Pair(rhs, lhs) => { +// let rhs = SnailfishNumberIterator::from(rhs.deref()); +// let lhs = SnailfishNumberIterator::from(lhs.deref()); +// let chain = rhs.chain(lhs); +// chain.nth(self.index) +// }, +// SnailfishNumber::Regular(n) => { +// if self.index == 0 { +// Some(n) +// } else { +// None +// } +// } +// }; +// if let Some(next) = next { +// self.index += 1; +// Some(next) +// } else { +// None +// } +// } +//} + +impl SnailfishNumber { + pub fn regular(&self) -> Option { + match self { + SnailfishNumber::Regular(n) => Some(*n), + _ => None, + } + } + + pub fn magnitude(&self) -> isize { + match self { + SnailfishNumber::Pair(lhs, rhs) => 3 * lhs.magnitude() + 2 * rhs.magnitude(), + SnailfishNumber::Regular(reg) => *reg, + } + } + + pub fn reduce(self) -> Self { + let mut new = self; + while let (changed, true) = new.reduce_once() { + new = changed; + } + new + } + + pub fn reduce_once(&self) -> (Self, bool) { + if let (_, changed, _, true) = self.explode(0) { + println!("after explode: {:?}", &changed); + (changed, true) + } else if let (changed, true) = self.split() { + println!("after split: {:?}", &changed); + (changed, true) + } else { + (self.clone(), false) + } + } + + pub fn split(&self) -> (Self, bool) { + match self { + Self::Regular(n) if n >= &10 => { + let lhs = ((*n as f64) / 2.0).floor() as isize; + let rhs = ((*n as f64) / 2.0).ceil() as isize; + ((lhs, rhs).into(), true) + } + Self::Regular(n) => (n.into(), false), + Self::Pair(lhs, rhs) => match lhs.split() { + (lhs, true) => ((lhs, rhs).into(), true), + (_, false) => match rhs.split() { + (rhs, true) => (Self::Pair(lhs.clone(), Box::new(rhs)), true), + (_, false) => (self.clone(), false), + }, + }, + } + } + + pub fn explode(&self, depth: usize) -> (Option, Self, Option, bool) { + let new = self.clone(); + match new { + SnailfishNumber::Pair(mut lhs, mut rhs) => { + if depth == 4 { + let (lhs, rhs) = (lhs.regular().unwrap(), rhs.regular().unwrap()); + (Some(lhs), 0.into(), Some(rhs), true) + } else { + let (left_addition, inner, right_addition, exploded) = lhs.explode(depth + 1); + if exploded { + if let Some(right_addition) = right_addition { + rhs.add_right(right_addition); + } + return (left_addition, (inner, rhs).into(), None, true); + } + let (left_addition, inner, right_addition, exploded) = rhs.explode(depth + 1); + if exploded { + if let Some(left_addition) = left_addition { + lhs.add_left(left_addition); + } + return (None, (lhs, inner).into(), right_addition, true); + } + (None, self.clone(), None, false) + } + } + regular => (None, regular, None, false), + } + } + + fn add_left(&mut self, other: isize) { + match self { + Self::Pair(_, rhs) => rhs.add_left(other), + Self::Regular(n) => *n += other, + } + } + fn add_right(&mut self, other: isize) { + match self { + Self::Pair(lhs, _) => lhs.add_right(other), + Self::Regular(n) => *n += other, + } + } +} + +impl Add for SnailfishNumber { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self::from((self, rhs)).reduce() + } +} + +#[cfg(test)] +mod test { + use super::SnailfishNumber; + + #[test] + fn multiple_reductions_example() { + let lhs: SnailfishNumber = ((((4, 3), 4), 4), (7, ((8, 4), 9))).into(); + let rhs: SnailfishNumber = (1, 1).into(); + + let result: SnailfishNumber = ((((0, 7), 4), ((7, 8), (6, 0))), (8, 1)).into(); + assert_eq!(lhs + rhs, result); + } + + #[test] + fn explosion_1() { + let number: SnailfishNumber = (((((9, 8), 1), 2), 3), 4).into(); + let result: SnailfishNumber = ((((0, 9), 2), 3), 4).into(); + assert_eq!(number.reduce(), result); + } + + #[test] + fn explosion_2() { + let number: SnailfishNumber = (7, (6, (5, (4, (3, 2))))).into(); + let result: SnailfishNumber = (7, (6, (5, (7, 0)))).into(); + assert_eq!(number.reduce(), result); + } + + #[test] + fn explosion_3() { + let number: SnailfishNumber = ((6, (5, (4, (3, 2)))), 1).into(); + let result: SnailfishNumber = ((6, (5, (7, 0))), 3).into(); + assert_eq!(number.reduce(), result); + } + + #[test] + fn explosion_4() { + let number: SnailfishNumber = ((3, (2, (1, (7, 3)))), (6, (5, (4, (3, 2))))).into(); + let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into(); + assert_eq!(number.reduce_once(), (result, true)); + } + + #[test] + fn explosion_5() { + let number: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into(); + let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (7, 0)))).into(); + assert_eq!(number.reduce(), result); + } + + #[test] + fn magnitude() { + let number: SnailfishNumber = ( + (((8, 7), (7, 7)), ((8, 6), (7, 7))), + (((0, 7), (6, 6)), (8, 7)), + ) + .into(); + assert_eq!(number.magnitude(), 3488); + } +} diff --git a/src/day_18/parsing.rs b/src/day_18/parsing.rs new file mode 100644 index 0000000..28071be --- /dev/null +++ b/src/day_18/parsing.rs @@ -0,0 +1,68 @@ +use yap::Tokens; + +use crate::parsing::{newline, parse_digit}; + +use super::model::SnailfishNumber; + +pub fn parse_regular(tokens: &mut impl Tokens) -> Option { + parse_digit(tokens).map(|digit: isize| digit.into()) +} + +pub fn parse_pair(tokens: &mut impl Tokens) -> Option { + tokens.optional(|t| { + if !t.token('[') { + return None; + } + let lhs = parse_snailfish_number(t)?; + if !t.token(',') { + return None; + } + let rhs = parse_snailfish_number(t)?; + if !t.token(']') { + return None; + } + Some((lhs, rhs).into()) + }) +} + +pub fn parse_snailfish_number(tokens: &mut impl Tokens) -> Option { + yap::one_of!(t from tokens; + parse_regular(t), + parse_pair(t), + ) +} + +pub fn parse_number_list(tokens: &mut impl Tokens) -> Option> { + tokens.optional(|t| { + let vec: Vec = t + .sep_by(|t| parse_snailfish_number(t), |t| newline(t)) + .collect(); + if !vec.is_empty() { + Some(vec) + } else { + None + } + }) +} + +#[cfg(test)] +mod test { + use yap::IntoTokens; + + #[test] + fn test_parsing() { + let number = super::parse_snailfish_number( + &mut "[[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]]".into_tokens(), + ); + assert_eq!( + number, + Some( + ( + (((1, 3), (5, 3)), ((1, 3), (8, 7))), + (((4, 9), (6, 9)), ((8, 2), (7, 3))) + ) + .into() + ) + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 0f9c2b1..79f09a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,5 +18,6 @@ pub mod day_13; pub mod day_14; pub mod day_15; pub mod day_16; +pub mod day_18; aoc_lib! { year = 2021 }