use color_eyre::eyre; #[derive(Debug, Clone)] enum Node { Leaf(u8), Branch { left: Box, right: Box, } } use Node::*; impl 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(), } } fn magnitude(&self) -> usize { match self { Node::Leaf(v) => *v as usize, Node::Branch {left, right} => 3 * left.magnitude() + 2 * right.magnitude(), } } } struct Parser<'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 to avoid nesting too deeply Parser {chars} } fn parse(&mut self) -> Node { let mut left = None; loop { let node = match self.chars.next() { Some('[') => self.parse(), Some(','|']') => {continue;}, Some(c) => Node::Leaf(c.to_digit(10).unwrap() as u8), None => {dbg!(left); panic!("Ran out of characters while parsing");}, }; if left.is_none() { left = Some(node); } else { return Node::Branch { left: Box::new(left.unwrap()), right: Box::new(node), }; } } } } fn load(data: &str) -> Vec { let mut nodes = Vec::new(); for line in data.lines() { let mut parser = Parser::from(line); nodes.push(parser.parse()); } nodes } fn explode(node: &mut Node, depth: usize) -> Option<(u8, u8)> { // only branch nodes can explode if let Leaf(_) = node {return None;} if depth >= 4 { 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 true; }, Leaf(_) => return false, Branch {left, right} => { if split(left) { return true; } else { return split(right); } } } } fn reduce(node: &mut Node) { loop { if let Some(..) = explode(node, 0) { continue; } if split(node) { continue; } break; } } fn add(left: Node, right: Node) -> Node { let mut sum = Node::Branch { left: Box::new(left), right: Box::new(right), }; reduce(&mut sum); sum } 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(); // 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((one.magnitude(), max)) }