implement day 18

This commit is contained in:
Jan Christian Grünhage 2021-12-18 17:28:33 +01:00
parent 71be88574c
commit c49814908e
4 changed files with 395 additions and 0 deletions

68
src/day_18/mod.rs Normal file
View file

@ -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<SnailfishNumber> {
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);
}
}

258
src/day_18/model.rs Normal file
View file

@ -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<SnailfishNumber>, Box<SnailfishNumber>),
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<T> From<T> for SnailfishNumber
where
T: Borrow<Box<SnailfishNumber>>,
{
fn from(s: T) -> Self {
s.borrow().deref().clone()
}
}
impl From<isize> for SnailfishNumber {
fn from(s: isize) -> Self {
Self::Regular(s)
}
}
impl From<&isize> for SnailfishNumber {
fn from(s: &isize) -> Self {
Self::Regular(*s)
}
}
impl<LHS, RHS> From<(LHS, RHS)> for SnailfishNumber
where
LHS: Into<SnailfishNumber>,
RHS: Into<SnailfishNumber>,
{
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<Self::Item> {
// 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<isize> {
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<isize>, Self, Option<isize>, 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);
}
}

68
src/day_18/parsing.rs Normal file
View file

@ -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<Item = char>) -> Option<SnailfishNumber> {
parse_digit(tokens).map(|digit: isize| digit.into())
}
pub fn parse_pair(tokens: &mut impl Tokens<Item = char>) -> Option<SnailfishNumber> {
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<Item = char>) -> Option<SnailfishNumber> {
yap::one_of!(t from tokens;
parse_regular(t),
parse_pair(t),
)
}
pub fn parse_number_list(tokens: &mut impl Tokens<Item = char>) -> Option<Vec<SnailfishNumber>> {
tokens.optional(|t| {
let vec: Vec<SnailfishNumber> = 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()
)
);
}
}

View file

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