diff --git a/Cargo.toml b/Cargo.toml index 1438192..b85788e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,3 +70,7 @@ path = "src/day16.rs" [[bin]] name = "day17" path = "src/day17.rs" + +[[bin]] +name = "day18" +path = "src/day18.rs" diff --git a/inputs/day18.txt b/inputs/day18.txt new file mode 100644 index 0000000..9b65fac --- /dev/null +++ b/inputs/day18.txt @@ -0,0 +1,100 @@ +[[[[7,1],[0,0]],[6,[8,2]]],[8,[3,8]]] +[[[3,6],[9,4]],[[[5,9],5],[8,0]]] +[[[2,2],2],[1,[[1,6],7]]] +[[[[0,9],7],[[3,2],8]],[6,[7,9]]] +[[[[4,1],6],[[7,6],[2,2]]],[[[1,1],9],4]] +[[[8,[3,7]],3],[[4,4],[[9,1],[3,5]]]] +[[4,[8,2]],[1,[0,5]]] +[8,[8,7]] +[[[[2,2],7],[3,[4,5]]],[[4,6],[[2,5],4]]] +[[[5,5],[[5,1],3]],[[2,[8,2]],[[6,9],[1,5]]]] +[0,7] +[[[[5,1],3],[8,[5,3]]],7] +[[5,[2,[0,6]]],[[[5,5],2],[9,[8,0]]]] +[[[[3,4],2],0],4] +[[[[5,3],[2,7]],6],[[4,0],[9,[7,2]]]] +[[[3,[2,5]],[3,3]],7] +[[[[5,1],1],[4,8]],[[5,[8,3]],2]] +[[4,[[8,1],[8,5]]],[[[4,1],0],6]] +[[[5,5],[5,9]],[0,[[6,8],[0,1]]]] +[4,[[[7,9],4],0]] +[[[[0,1],7],[[3,6],5]],[8,[5,[6,1]]]] +[[[7,7],[8,0]],[6,[8,[7,9]]]] +[[[9,2],1],6] +[[[4,4],[2,[5,0]]],[[[2,6],6],[5,[4,3]]]] +[[2,[[4,7],5]],1] +[[8,7],[[[2,0],7],[1,[0,3]]]] +[[9,[[9,3],[9,5]]],[[8,7],[[4,1],[6,5]]]] +[[3,4],[[9,4],5]] +[[5,[[8,3],5]],1] +[[0,[[9,0],[3,2]]],[2,[7,[5,1]]]] +[[9,[[9,5],[8,6]]],[[4,4],[[3,8],[1,6]]]] +[[[1,[5,2]],9],[[4,6],[3,[8,0]]]] +[[1,7],[[1,7],9]] +[[[[3,4],3],[[7,5],[9,1]]],[[[5,0],[3,0]],[[7,9],6]]] +[[[7,2],[[1,0],[5,6]]],[[[3,7],[8,9]],6]] +[[[[1,1],1],[[8,6],[9,8]]],[[[1,8],4],[8,9]]] +[[[8,9],0],3] +[[[1,7],[1,[3,9]]],[6,[0,[8,5]]]] +[[0,5],[6,5]] +[[[[6,8],[4,5]],[[7,4],6]],[[3,6],5]] +[[8,[[0,9],8]],[9,[7,[7,9]]]] +[0,[[[7,1],2],[[0,4],4]]] +[[0,[[9,1],5]],[1,4]] +[3,4] +[[[9,3],[1,3]],[[[4,8],3],[[1,3],[9,0]]]] +[[[[5,1],7],[[9,2],8]],[[[6,8],[5,4]],[0,1]]] +[8,[[1,[3,0]],[[7,9],4]]] +[[[6,4],[[2,9],[9,0]]],[7,[[0,0],3]]] +[[3,[[9,6],6]],2] +[[5,[[3,1],[7,5]]],[[[6,7],9],[[4,6],[5,2]]]] +[[[4,[6,5]],8],[[6,[8,0]],[[9,3],3]]] +[[[[4,9],[2,8]],9],[[[5,0],0],[[3,4],[2,8]]]] +[[3,[7,1]],[9,[[1,8],7]]] +[[9,1],[0,[[0,7],[7,1]]]] +[[7,[0,[7,6]]],[[[5,3],1],[6,[4,5]]]] +[8,[[[2,1],[6,9]],[[3,3],[4,6]]]] +[0,[7,[3,0]]] +[[[[1,6],3],[5,[8,0]]],[[[6,6],7],1]] +[[[7,[8,3]],3],[[[2,8],5],[0,[9,5]]]] +[[[[5,1],4],[[1,2],1]],7] +[[[3,[7,5]],7],3] +[[9,[6,[1,1]]],[[[4,1],[2,2]],[[9,5],[7,7]]]] +[2,7] +[[[9,[8,6]],[[9,0],[6,5]]],[[[6,7],5],[[7,7],[2,3]]]] +[[[0,[6,4]],2],[4,[7,[7,5]]]] +[[[[6,1],[9,1]],[[6,1],9]],[[2,6],0]] +[[0,[[1,8],[3,5]]],[4,[[8,2],[4,2]]]] +[[[[9,3],[4,2]],2],[[[2,1],[7,1]],[4,8]]] +[[[3,[0,2]],3],8] +[[[4,[4,9]],9],[[[4,4],5],9]] +[[[[8,2],7],9],[[[1,0],[3,8]],[[7,7],0]]] +[[[3,2],[9,7]],[[9,[8,2]],[[5,5],3]]] +[[[7,[3,1]],[[8,3],1]],[[[8,6],[7,0]],4]] +[[9,[[9,1],5]],[[4,[1,1]],2]] +[[[[7,4],[0,3]],7],[8,[6,[3,3]]]] +[5,5] +[[6,7],[1,[7,[8,1]]]] +[[1,[0,4]],7] +[[[4,0],[[0,1],[2,2]]],[9,[[9,9],[3,0]]]] +[[[6,0],[[8,6],3]],[[5,1],[[8,1],[2,7]]]] +[[[[8,3],7],5],[9,[[5,1],8]]] +[[[[4,0],[5,2]],[[0,0],7]],2] +[[[[0,1],6],2],[[8,2],6]] +[[[[2,4],1],[[6,7],9]],[[[1,6],9],3]] +[[5,5],[[8,[7,7]],[5,8]]] +[[6,[[9,2],[9,7]]],[[[8,5],[4,4]],7]] +[[[9,[7,7]],[6,0]],[7,[[8,7],[1,2]]]] +[[7,[6,2]],[[9,[5,2]],[1,4]]] +[[[7,[5,9]],[[3,9],[4,5]]],[0,6]] +[[9,[8,[2,2]]],[[9,7],[1,1]]] +[[[[2,3],4],[[4,8],9]],[[9,[8,6]],[[0,9],0]]] +[[0,[[9,3],0]],[8,8]] +[[[[2,9],6],[[2,8],9]],[[[0,5],6],[[6,1],7]]] +[[9,[[8,3],[5,8]]],[[7,[3,0]],3]] +[[[4,[4,2]],0],1] +[[[[9,6],[5,8]],[6,2]],[[[8,0],[7,0]],[[5,6],4]]] +[[[8,0],[[4,3],[7,4]]],[[3,[7,9]],[[7,3],6]]] +[[3,[5,[0,3]]],[5,4]] +[[[[1,2],[6,3]],1],[[7,[5,2]],[[8,8],7]]] +[[4,[[8,0],[7,1]]],[[8,[8,0]],[[1,5],3]]] diff --git a/src/day18.rs b/src/day18.rs new file mode 100644 index 0000000..acd2cf5 --- /dev/null +++ b/src/day18.rs @@ -0,0 +1,241 @@ +use std::fmt; +use std::ops::Add; +use std::str::FromStr; + +#[derive(Clone)] +enum Value { + Number(u32, usize), + Pair(Box, Box), +} + +impl Value { + fn new_pair(left: Value, right: Value) -> Self { + Value::Pair(Box::new(left), Box::new(right)) + } +} + +impl Value { + fn is_number_pair(&self) -> bool { + match self { + Value::Pair(left, right) => left.is_number() && right.is_number(), + Value::Number(_, _) => false, + } + } + + fn is_number(&self) -> bool { + matches!(*self, Value::Number(_, _)) + } + + fn number(&self) -> Option { + match self { + Self::Number(n, _) => Some(*n), + _ => None + } + } + + fn index(&self) -> Option { + match self { + Self::Number(_, i) => Some(*i), + _ => None + } + } + + fn add_number(&mut self, n: u32) { + if let Self::Number(m, i) = self { + *self = Value::Number(*m + n, *i); + } + } +} + +impl Add for Value { + type Output = Self; + + fn add(self, other: Self) -> Self { + let mut v = Value::new_pair(self, other); + reduce(&mut v); + v + } +} + +fn parse_pair(iter: &mut std::iter::Peekable) -> Value { + if iter.next().unwrap() != '[' { + panic!(); + } + let left = match iter.peek().unwrap() { + '[' => parse_pair(iter), + '0'..='9' => Value::Number(iter.next().unwrap().to_digit(10).unwrap(), 0), + _ => panic!(), + }; + if iter.next().unwrap() != ',' { + panic!(); + } + let right = match iter.peek().unwrap() { + '[' => parse_pair(iter), + '0'..='9' => Value::Number(iter.next().unwrap().to_digit(10).unwrap(), 0), + _ => panic!(), + }; + if iter.next().unwrap() != ']' { + panic!(); + } + Value::new_pair(left, right) +} + +impl FromStr for Value { + type Err = (); + + fn from_str(s: &str) -> Result { + let mut v = parse_pair(&mut s.chars().peekable()); + renumber(&mut v); + Ok(v) + } +} + +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Value::Number(n, _) => write!(f, "{}", n), + Value::Pair(left, right) => write!(f, "[{},{}]", left, right), + } + } +} + +fn find_exploded(tree: &Value, depth: usize) -> Option<&Value> { + if let Value::Pair(left, right) = tree { + if depth == 4 && tree.is_number_pair() { + Some(tree) + } else { + find_exploded(left, depth + 1) + .or_else(|| find_exploded(right, depth + 1)) + } + } else { + None + } +} + +fn find_number(tree: &mut Value, index: usize, n: u32) -> Option<&Value> { + match tree { + Value::Pair(left, right) => find_number(left, index, n).or_else(|| find_number(right, index, n)), + Value::Number(_, i) if *i == index => { + tree.add_number(n); + Some(tree) + }, + _ => None, + } +} + +fn zero_pair(tree: &mut Value, index: usize) { + if let Value::Pair(left, right) = tree { + if let Value::Pair(ref n, _) = **left { + if let Value::Number(_, i) = **n { + if i == index { + *left = Box::new(Value::Number(0, 0)); + return; + } + } + } + if let Value::Pair(ref n, _) = **right { + if let Value::Number(_, i) = **n { + if i == index { + *right = Box::new(Value::Number(0, 0)); + return; + } + } + } + zero_pair(left, index); + zero_pair(right, index); + } +} + +fn renumber_pair(value: &mut Value, counter: &mut usize) { + if let Value::Pair(left, right) = value { + renumber_pair(left.as_mut(), counter); + renumber_pair(right.as_mut(), counter); + } + if let Value::Number(n, _) = value { + *value = Value::Number(*n, *counter); + *counter += 1; + } +} + +fn renumber(tree: &mut Value) { + let mut counter = 0; + renumber_pair(tree, &mut counter); +} + +fn explode(tree: &mut Value) { + let exploded = &mut find_exploded(tree, 0); + if let Some(value) = exploded { + if let Value::Pair(left, right) = value { + let index = left.index().unwrap(); + let left_n = left.number().unwrap(); + let right_n = right.number().unwrap(); + + zero_pair(tree, index); + + if index > 0 { + find_number(tree, index - 1, left_n); + } + find_number(tree, index + 2, right_n); + renumber(tree); + } else { + panic!(); + } + } +} + +fn reduce(value: &mut Value) -> &Value { + explode(value); + value +} + +fn magnitude(value: Value) -> u64 { + match value { + Value::Number(n, _) => n as u64, + Value::Pair(left, right) => 3 * magnitude(*left) + 2 * magnitude(*right), + } +} + +fn main() { + const INPUT: &str = include_str!("../inputs/day18.txt"); + let reduced = INPUT + .lines() + .map(|line| line.parse().unwrap()) + .reduce(|u: Value, v| u + v) + .unwrap(); + println!("solution {}", magnitude(reduced)); +} + +#[test] +fn test_explode_left() { + let mut v: Value = "[[[[[9,8],1],2],3],4]".parse().unwrap(); + assert_eq!(format!("{}", reduce(&mut v)), "[[[[0,9],2],3],4]"); +} + +#[test] +fn test_explode_right() { + let mut v: Value = "[7,[6,[5,[4,[3,2]]]]]".parse().unwrap(); + assert_eq!(format!("{}", reduce(&mut v)), "[7,[6,[5,[7,0]]]]"); +} + +#[test] +fn test_explode_left_again() { + let mut v: Value = "[[6,[5,[4,[3,2]]]],1]".parse().unwrap(); + assert_eq!(format!("{}", reduce(&mut v)), "[[6,[5,[7,0]]],3]"); +} + +#[test] +fn test_explode() { + let mut v: Value = "[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]".parse().unwrap(); + assert_eq!( + format!("{}", reduce(&mut v)), + "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]" + ); +} + +#[test] +fn test_magnitude() { + let v: Value = "[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]" + .parse() + .unwrap(); + assert_eq!(magnitude(v), 3488); +}