From 61374b8655396eb757e2018d67400deba40e93f0 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Fri, 7 Jan 2022 14:57:22 -0800 Subject: [PATCH] completely rework day 18 --- 2021/src/day18.rs | 226 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 174 insertions(+), 52 deletions(-) diff --git a/2021/src/day18.rs b/2021/src/day18.rs index 77993d8..888f8f8 100644 --- a/2021/src/day18.rs +++ b/2021/src/day18.rs @@ -1,90 +1,212 @@ -// use std::iter::Peekable; -use std::str::Chars; - use color_eyre::eyre; -use eyre::eyre; #[derive(Debug)] -enum Item { - Value(u8), - SubPair(Box), -} - -impl Item { - fn magnitude(&self) -> usize { - match self { - Item::Value(v) => *v as usize, - Item::SubPair(pair) => pair.magnitude(), - } +enum Node { + Leaf(u8), + Branch { + left: Box, + right: Box, } } -#[derive(Debug)] -struct Pair { - left: Item, - right: Item, -} +impl Node { + fn is_leaf(&self) -> bool { + match self { + Node::Leaf(_) => true, + Node::Branch {..} => false, + } + } + + fn is_branch(&self) -> bool { + match self { + Node::Leaf(_) => false, + Node::Branch {..} => true, + } + } + + fn unwrap_value(&self) -> u8 { + match self { + Node::Leaf(v) => *v, + Node::Branch {..} => panic!("Attempted to unwrap value from a branch node"), + } + } + + fn value_mut(&mut self) -> &mut u8 { + match self { + Node::Leaf(v) => v, + Node::Branch {..} => panic!("Attempted to unwrap value from a branch node"), + } + } + + fn unwrap_branch(&self) -> (&Node, &Node) { + match self { + Node::Leaf(_) => panic!("Attempted to unwrap branches from a leaf node"), + Node::Branch {left, right} => (left, right), + } + } + + fn leftmost_value_mut(&mut self) -> &mut u8 { + match self { + Node::Leaf(val) => val, + Node::Branch {left, ..} => left.leftmost_value_mut(), + } + } + + fn rightmost_value_mut(&mut self) -> &mut u8 { + match self { + Node::Leaf(val) => val, + Node::Branch {right, ..} => right.rightmost_value_mut(), + } + } -impl Pair { fn magnitude(&self) -> usize { - 3 * self.left.magnitude() + 2 * self.right.magnitude() + match self { + Node::Leaf(v) => *v as usize, + Node::Branch {left, right} => 3 * left.magnitude() + 2 * right.magnitude(), + } } } struct Parser<'a> { - chars: Chars<'a>, + chars: std::str::Chars<'a>, } impl<'a> Parser<'a> { fn from(src: &'a str) -> Parser<'a> { let mut chars = src.chars(); - chars.next(); // throw away the opening bracket so we don't nest too deeply + chars.next(); // throw away the opening bracket to avoid nesting too deeply Parser {chars} } - fn parse(&mut self) -> eyre::Result { - let mut items = Vec::with_capacity(2); - while items.len() < 2 { - let item = match self.chars.next() { - Some('[') => Item::SubPair(Box::new(self.parse()?)), + fn parse(&mut self) -> Node { + let mut left = None; + loop { + let node = match self.chars.next() { + Some('[') => self.parse(), Some(','|']') => {continue;}, - Some(c) => Item::Value(c.to_digit(10).unwrap() as u8), - None => {return Err(eyre!("Ran out of characters to parse"));}, + Some(c) => Node::Leaf(c.to_digit(10).unwrap() as u8), + None => {dbg!(left); panic!("Ran out of characters while parsing");}, }; - items.push(item); - } - let right = items.pop().unwrap(); - let left = items.pop().unwrap(); - Ok(Pair {left, right}) + if left.is_none() { + left = Some(node); + } + else { + return Node::Branch { + left: Box::new(left.unwrap()), + right: Box::new(node), + }; + } + } } } - -fn load(data: &str) -> eyre::Result>{ - let mut pairs = Vec::new(); +fn load(data: &str) -> Vec { + let mut nodes = Vec::new(); for line in data.lines() { let mut parser = Parser::from(line); - let pair = parser.parse() - .map_err(|_| eyre!("Invalid input: {}", line))?; - pairs.push(pair); + nodes.push(parser.parse()); } - Ok(pairs) + nodes } -const _SAMPLE: &str = "[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]"; +#[derive(Debug)] +enum ReduceOp { + Noop, + Split, + Explode(u8, u8), +} -pub fn run(data: &str) -> eyre::Result<(usize, usize)> { - // let pairs = load(data)?; - // dbg!(pairs.len()); - // dbg!(&pairs[0]); - let mut parser = Parser::from(_SAMPLE); - let root = parser.parse()?; - dbg!(root.magnitude()); +fn walk(node: &mut Node, depth: usize) -> ReduceOp { + // just to improve readability + use {Node::*, ReduceOp::*}; + + if depth >= 4 { + if let Branch {left, right} = node { + let v1 = left.unwrap_value(); + let v2 = right.unwrap_value(); + *node = Leaf(0); + return Explode(v1, v2); + } + } + + match node { + Leaf(v) if *v > 9 => { + *node = Branch { + left: Box::new(Leaf(*v / 2)), + right: Box::new(Leaf((*v + 1) / 2)), + }; + return Split + }, + Leaf(_) => return Noop, + // here's where it gets complicated + Branch {left, right} => { + // keep walking, depth-first and left-first + let res = walk(left, depth + 1); + // if a sub-node exploded, and our right node is a leaf, add the right half of the explode to our right leaf + match res { + Explode(v1, v2) if v2 > 0 => { + *right.leftmost_value_mut() += v2; + // and propagate the explode up again, except we've already used v2 so replace it with 0 + return Explode(v1, 0); + }, + Explode(..)|Split => return res, + Noop => (), + } + + // if no children of the left branch exploded, keep going with the right + let res = walk(right, depth + 1); + // same concept, except we're adding the left half of the sub-explode to our left leaf (if it is a leaf) + match res { + Explode(v1, v2) if v1 > 0 => { + *left.rightmost_value_mut() += v1; + return Explode(0, v2); + }, + Explode(..)|Split => return res, + Noop => (), + } + } + } + // if everything else completes, then there were no splits or explodes below us in the tree + return Noop +} + + +fn reduce(node: &mut Node) { + while let ReduceOp::Explode(..)|ReduceOp::Split = walk(node, 0) {} +} + + +fn add(left: Node, right: Node) -> Node { + let mut sum = Node::Branch { + left: Box::new(left), + right: Box::new(right), + }; + reduce(&mut sum); + sum +} + + +const SAMPLE: &str = "[[1,2],[[3,4],5]] +[[[[0,7],4],[[7,8],[6,0]]],[8,1]] +[[[[1,1],[2,2]],[3,3]],[4,4]] +[[[[3,0],[5,3]],[4,4]],[5,5]] +[[[[5,0],[7,4]],[5,5]],[6,6]] +[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]"; + +pub fn run(_data: &str) -> eyre::Result<(usize, usize)> { + let nodes = load(SAMPLE); + + // let one = nodes.into_iter().reduce(|sum, next| add(sum, next)); + // dbg!(one); + + for node in nodes { + println!("{}", node.magnitude()); + } Ok((0, 0)) -} \ No newline at end of file +}