From 68cade10acd64470f1ba00a4641b5cc7bceab7bb Mon Sep 17 00:00:00 2001 From: kageru Date: Sat, 18 Dec 2021 17:06:34 +0100 Subject: [PATCH] Day 18: add split and tests --- 2021/src/bin/day18.rs | 130 +++++++++++++++++++++++++++++++++++------- 1 file changed, 109 insertions(+), 21 deletions(-) diff --git a/2021/src/bin/day18.rs b/2021/src/bin/day18.rs index 2b50383..b825690 100644 --- a/2021/src/bin/day18.rs +++ b/2021/src/bin/day18.rs @@ -1,25 +1,19 @@ +#![feature(result_option_inspect)] #![feature(box_syntax)] #![feature(test)] extern crate test; use aoc2021::common::*; -use std::{fmt, ops::Add}; +use std::fmt; const DAY: usize = 18; type Parsed = Vec; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] enum Node { Number(usize), Pair(Box<(Node, Node)>), } -impl Add for Node { - type Output = Self; - fn add(self, rhs: Self) -> Self::Output { - Node::Pair(box (self, rhs)) - } -} - impl fmt::Display for Node { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self { @@ -46,15 +40,37 @@ fn parse_node(raw: &str) -> (Node, usize) { } #[derive(Debug, PartialEq)] -enum Reduction { +enum Explosion { None, Partial, Full, } impl Node { + fn reduce(&mut self) { + while self.explode() || self.split() {} + } + fn explode(&mut self) -> bool { - self.explode_inner(0, &mut None, &mut None) != Reduction::None + self.explode_inner(0, &mut None, &mut None) != Explosion::None + } + + fn split(&mut self) -> bool { + match self { + Node::Number(n) if *n >= 10 => { + *self = Node::Pair(box (Node::Number(*n / 2), Node::Number(*n / 2 + (*n & 1)))); + true + } + Node::Pair(p) => p.0.split() || p.1.split(), + _ => false, + } + } + + fn magnitude(&self) -> usize { + match self { + Node::Number(n) => *n, + Node::Pair(p) => 3 * p.0.magnitude() + 2 * p.1.magnitude(), + } } fn explode_inner<'a, 'b>( @@ -62,16 +78,16 @@ impl Node { depth: usize, previous_number: &'b mut Option<&'a mut usize>, for_next: &'b mut Option, - ) -> Reduction { + ) -> Explosion { match (self, &for_next) { (Node::Number(n), Some(x)) => { *n += x; *for_next = None; - Reduction::Full + Explosion::Full } (Node::Number(n), None) => { *previous_number = Some(n); - Reduction::None + Explosion::None } (s @ Node::Pair(_), _) if depth == 4 => { let (left, right) = s.number_pair_or_panic(); @@ -80,11 +96,11 @@ impl Node { } *for_next = Some(*right); *s = Node::Number(0); - Reduction::Partial + Explosion::Partial } (Node::Pair(p), _) => { let left = p.0.explode_inner(depth + 1, previous_number, for_next); - if left != Reduction::Full { + if left != Explosion::Full { p.1.explode_inner(depth + 1, previous_number, for_next) } else { left @@ -109,7 +125,21 @@ impl Node { } fn part1(parsed: &Parsed) -> usize { - unimplemented!() + add_and_reduce(parsed.clone()).inspect(|n| println!("{n}")).unwrap().magnitude() +} + +fn add_and_reduce(parsed: Parsed) -> Option { + /* + let mut r = parsed.into_iter().reduce(move |acc, new| Node::Pair(box (acc, new))).unwrap(); + r.reduce(); + Some(r) + */ + parsed.into_iter().reduce(move |acc, new| { + let mut n = Node::Pair(box (acc, new)); + n.reduce(); + println!("{n}"); + n + }) } fn part2(parsed: &Parsed) -> usize { @@ -131,7 +161,7 @@ mod tests { const TEST_INPUT_SINGLE_ADDITION: &str = "[[[[4,3],4],4],[7,[[8,4],9]]] [1,1]"; - const TEST_INPUT: &str = "[1,2] + const OTHER_TEST_INPUT: &str = "[1,2] [[1,2],3] [9,[8,7]] [[1,9],[8,5]] @@ -139,6 +169,17 @@ mod tests { [[[9,[3,8]],[[0,9],6]],[[[3,7],[4,9]],3]] [[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]]"; + const TEST_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]]]"; + #[test] fn test_example_parsing() { let [first, second]: [Node; 2] = parse_input(TEST_INPUT_SINGLE_ADDITION).try_into().unwrap(); @@ -154,7 +195,7 @@ mod tests { #[test] fn test_node_display() { - for (actual, expected) in parse_input(TEST_INPUT).into_iter().zip(TEST_INPUT.lines()) { + for (actual, expected) in parse_input(OTHER_TEST_INPUT).into_iter().zip(OTHER_TEST_INPUT.lines()) { assert_eq!(expected, actual.to_string()); } } @@ -170,9 +211,56 @@ mod tests { i.to_string() } + #[test_case("[[1,2],[[3,4],5]]" => 143)] + #[test_case("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]" => 1384)] + #[test_case("[[[[1,1],[2,2]],[3,3]],[4,4]]" => 445)] + #[test_case("[[[[3,0],[5,3]],[4,4]],[5,5]]" => 791)] + #[test_case("[[[[5,0],[7,4]],[5,5]],[6,6]]" => 1137)] + #[test_case("[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]" => 3488)] + #[test_case("[[[[6,6],[7,6]],[[7,7],[7,0]]],[[[7,7],[7,7]],[[7,8],[9,9]]]]" => 4140)] + fn test_magnitude(raw: &str) -> usize { + parse_node(raw).0.magnitude() + } + + #[test] + fn test_full_chain() { + let lhs = parse_node("[[[[4,3],4],4],[7,[[8,4],9]]]").0; + let rhs = parse_node("[1,1]").0; + let mut res = Node::Pair(box (lhs, rhs)); + assert_eq!(res.to_string(), "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]"); + + let mut res2 = res.clone(); + + res.explode(); + assert_eq!(res.to_string(), "[[[[0,7],4],[7,[[8,4],9]]],[1,1]]"); + res.explode(); + assert_eq!(res.to_string(), "[[[[0,7],4],[15,[0,13]]],[1,1]]"); + res.split(); + assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[0,13]]],[1,1]]"); + res.split(); + assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[0,[6,7]]]],[1,1]]"); + res.explode(); + assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]"); + // should be done now + res.reduce(); + assert_eq!(res.to_string(), "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]"); + + // now again using .reduce() from the beginning + res2.reduce(); + assert_eq!(res, res2); + } + + #[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]" => "[[[[1,1],[2,2]],[3,3]],[4,4]]")] + #[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]\n[5,5]" => "[[[[3,0],[5,3]],[4,4]],[5,5]]")] + #[test_case("[1,1]\n[2,2]\n[3,3]\n[4,4]\n[5,5]\n[6,6]" => "[[[[5,0],[7,4]],[5,5]],[6,6]]")] + fn test_list_reduction(raw: &str) -> String { + let parsed = parse_input(raw); + add_and_reduce(parsed).unwrap().to_string() + } + + test!(part1() == 4140); + /* - test!(part1() == 0); - test!(part2() == 0); bench!(part1() == 0); bench!(part2() == 0); bench_input!(Vec::len => 0);