diff --git a/2021/src/day18.rs b/2021/src/day18.rs index 888f8f8..4853cb2 100644 --- a/2021/src/day18.rs +++ b/2021/src/day18.rs @@ -1,7 +1,7 @@ use color_eyre::eyre; -#[derive(Debug)] +#[derive(Debug, Clone)] enum Node { Leaf(u8), Branch { @@ -9,36 +9,9 @@ enum Node { right: Box, } } +use Node::*; 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"), @@ -113,71 +86,76 @@ fn load(data: &str) -> Vec { } -#[derive(Debug)] -enum ReduceOp { - Noop, - Split, - Explode(u8, u8), -} - - -fn walk(node: &mut Node, depth: usize) -> ReduceOp { - // just to improve readability - use {Node::*, ReduceOp::*}; +fn explode(node: &mut Node, depth: usize) -> Option<(u8, u8)> { + // only branch nodes can explode + if let Leaf(_) = node {return None;} 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); + let (left, right) = node.unwrap_branch(); + match (left, right) { + (Leaf(v1), Leaf(v2)) => { + let result = (*v1, *v2); + *node = Leaf(0); + return Some(result); + }, + _ => (), } } + if let Branch {left, right} = node { + if let Some((v1, v2)) = explode(left, depth + 1) { + if v2 > 0 { + *right.leftmost_value_mut() += v2; + return Some((v1, 0)); + } + return Some((v1, v2)); + } + + if let Some((v1, v2)) = explode(right, depth + 1) { + if v1 > 0 { + *left.rightmost_value_mut() += v1; + return Some((0, v2)); + } + return Some((v1, v2)); + } + } + + return None; +} + + +fn split(node: &mut Node) -> bool { match node { Leaf(v) if *v > 9 => { *node = Branch { left: Box::new(Leaf(*v / 2)), right: Box::new(Leaf((*v + 1) / 2)), }; - return Split + return true; }, - Leaf(_) => return Noop, - // here's where it gets complicated + Leaf(_) => return false, 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 split(left) { + return true; } - - // 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 => (), + else { + return split(right); } } } - // 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) {} + loop { + if let Some(..) = explode(node, 0) { + continue; + } + if split(node) { + continue; + } + break; + } } @@ -191,22 +169,24 @@ fn add(left: Node, right: Node) -> Node { } -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(data); + let one = nodes.into_iter().reduce(|sum, next| add(sum, next)).unwrap(); -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()); + // I'm sure there's a more efficient way to do this, but + let mut max = 0; + let nodes2 = load(data); + for i in 0..nodes2.len() { + for j in 0..nodes2.len() { + if i == j {continue;} + let a = nodes2[i].clone(); + let b = nodes2[j].clone(); + let magnitude = add(a, b).magnitude(); + if magnitude > max { + max = magnitude; + } + } } - Ok((0, 0)) + Ok((one.magnitude(), max)) }