implement day 18
This commit is contained in:
parent
71be88574c
commit
c49814908e
68
src/day_18/mod.rs
Normal file
68
src/day_18/mod.rs
Normal 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
258
src/day_18/model.rs
Normal 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
68
src/day_18/parsing.rs
Normal 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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,5 +18,6 @@ pub mod day_13;
|
||||||
pub mod day_14;
|
pub mod day_14;
|
||||||
pub mod day_15;
|
pub mod day_15;
|
||||||
pub mod day_16;
|
pub mod day_16;
|
||||||
|
pub mod day_18;
|
||||||
|
|
||||||
aoc_lib! { year = 2021 }
|
aoc_lib! { year = 2021 }
|
||||||
|
|
Loading…
Reference in a new issue