From a8509f694a1ff74b6c1e42ee1ae5a498391fada0 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 20 Dec 2021 19:09:10 -0500 Subject: [PATCH] Day 18 --- input/day18.txt | 100 +++++++++ src/day18.rs | 565 ++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 +- 3 files changed, 667 insertions(+), 1 deletion(-) create mode 100644 input/day18.txt create mode 100644 src/day18.rs diff --git a/input/day18.txt b/input/day18.txt new file mode 100644 index 0000000..0e53f1b --- /dev/null +++ b/input/day18.txt @@ -0,0 +1,100 @@ +[[[[8,1],8],[[8,1],0]],[[8,[2,4]],[0,8]]] +[[[[2,2],[7,4]],[[9,1],6]],8] +[[[3,6],[[8,7],[2,9]]],[8,[[2,3],9]]] +[[[[4,5],[1,4]],1],[[0,8],[2,[6,8]]]] +[[7,[[4,6],[0,0]]],[[4,3],5]] +[[[[8,4],4],[[4,4],[1,0]]],[[5,[5,5]],[[5,2],1]]] +[[[0,[5,8]],[1,7]],[[[5,0],[1,3]],7]] +[4,[[[6,2],[7,8]],[0,[4,4]]]] +[[[3,[5,3]],8],[[[6,8],4],[8,9]]] +[[[6,0],[9,[8,1]]],[[[9,7],3],0]] +[[9,[[9,3],[0,8]]],[[[5,3],0],[[5,6],2]]] +[[3,[[7,7],3]],[[7,[5,2]],[[6,9],0]]] +[1,[4,[3,8]]] +[[[[0,2],9],[[0,7],8]],[[5,4],[2,8]]] +[[[[1,8],9],[1,7]],[[4,[8,5]],[[6,3],[1,0]]]] +[[9,[[4,3],[3,3]]],[[[4,9],[0,9]],6]] +[7,[[8,0],[5,6]]] +[[[[3,2],1],[[4,9],6]],[[9,[1,1]],[[8,7],1]]] +[[[[5,1],[3,3]],0],[1,[[3,2],2]]] +[[7,9],[[9,9],[5,[9,5]]]] +[[[[4,3],[1,7]],[4,[9,2]]],[[6,[1,7]],[[8,0],3]]] +[[[5,[2,8]],[[1,2],7]],[[3,[0,5]],[[3,5],8]]] +[[[[2,2],6],[[2,1],7]],[[[4,6],8],7]] +[[2,[[3,0],[0,5]]],[[[3,4],[0,1]],0]] +[[[[9,9],5],[[9,9],6]],[[[4,1],2],0]] +[4,[[2,9],[6,2]]] +[[[8,6],6],7] +[[7,[8,2]],[[[5,5],6],[9,0]]] +[5,[[2,5],[[4,9],[8,6]]]] +[[4,[7,[9,6]]],7] +[[[9,[3,3]],[[3,1],[8,7]]],[[6,[3,5]],[4,1]]] +[[8,6],[8,[[0,2],[8,1]]]] +[6,[8,[[7,7],0]]] +[3,4] +[[9,[8,0]],[[[7,8],3],1]] +[5,[[3,[8,7]],[[5,0],[9,7]]]] +[[[[4,2],9],[6,[0,2]]],6] +[[4,[3,[4,9]]],[[4,[1,6]],1]] +[[[6,3],[8,8]],[5,[[9,3],[6,3]]]] +[[[9,9],[[7,1],6]],[[[1,0],[7,4]],[3,[2,0]]]] +[[[[2,5],9],[3,[6,2]]],[4,7]] +[[1,[7,8]],[[[0,1],8],[[1,1],9]]] +[[[9,[6,4]],[[9,8],[0,2]]],[[[8,9],[2,3]],[3,[8,0]]]] +[[[[6,8],2],3],[[2,2],5]] +[[[4,[8,5]],[[4,3],1]],[[[2,4],[4,4]],[[4,1],[1,7]]]] +[[[[2,6],6],[[9,2],4]],[[[9,9],[9,5]],5]] +[[[[7,5],[4,9]],4],[[[0,7],[3,6]],[[6,5],[3,0]]]] +[[[4,4],[[5,7],[8,5]]],[0,8]] +[[3,[[1,3],[7,5]]],[6,[[8,1],0]]] +[[[9,9],[5,[9,6]]],[[[4,0],[5,4]],6]] +[0,[[[9,2],4],3]] +[[[1,[8,5]],[0,[6,0]]],[[[6,5],[3,1]],[[6,2],[1,5]]]] +[[[4,0],[4,7]],6] +[1,[[[5,2],9],[[3,9],4]]] +[[[[9,6],[4,1]],4],[2,[[0,2],6]]] +[9,[[[1,5],[3,1]],1]] +[5,0] +[9,[[[7,5],[2,1]],[[2,3],[5,3]]]] +[[5,[[0,5],[9,5]]],[[[2,7],3],[[2,9],[3,5]]]] +[[[1,9],2],[[7,[1,7]],[8,[9,8]]]] +[[8,9],[[5,[9,0]],[[6,8],[5,2]]]] +[6,[[[1,3],[0,8]],4]] +[[[[9,8],[0,9]],[[8,4],[3,5]]],[[[5,0],8],[[6,8],1]]] +[[6,[[1,4],[7,0]]],[[3,4],[[2,1],[2,7]]]] +[[[5,0],[3,[4,7]]],[[9,3],[[9,4],[9,6]]]] +[[[[8,3],[8,0]],5],[[[5,5],[0,2]],[[0,1],9]]] +[[[[6,4],[1,8]],[3,[0,2]]],[8,[[8,8],5]]] +[2,[[2,1],[1,4]]] +[8,[0,[3,5]]] +[[[[0,2],3],[[4,9],[1,2]]],[[8,2],[6,[7,1]]]] +[[[0,0],9],1] +[8,[[4,1],[[1,3],9]]] +[[[8,[5,9]],9],[[[5,7],[9,0]],3]] +[[5,[2,9]],7] +[5,6] +[[[[7,5],[8,3]],[[4,3],8]],[[2,2],[[7,2],[4,2]]]] +[[[9,5],[3,[1,5]]],6] +[[[[7,4],[7,9]],[[3,1],[3,1]]],[[[6,4],[0,1]],1]] +[[3,[[7,4],9]],[[[5,8],[2,7]],[[0,4],[3,6]]]] +[[[3,[2,3]],[[6,0],[7,7]]],1] +[[2,[[8,8],[2,3]]],[5,2]] +[[[0,[5,5]],[8,1]],5] +[[3,9],[6,[[0,5],[1,7]]]] +[[[[3,0],9],[8,2]],[[[2,2],8],0]] +[[[9,6],[[5,1],[4,9]]],[[[1,1],[0,3]],[[4,9],[7,5]]]] +[[[2,[6,1]],[[5,7],[9,2]]],[[[4,2],8],9]] +[[9,[7,1]],[[4,5],[9,1]]] +[[9,[5,0]],[[1,7],[[9,6],[4,5]]]] +[[[[1,1],[8,7]],4],[[0,4],[[1,7],[3,5]]]] +[[5,[1,[8,4]]],[[[9,4],0],[1,[5,5]]]] +[[[5,[1,6]],[6,0]],[[0,[9,7]],1]] +[2,[9,[[0,3],[2,3]]]] +[3,[4,[[0,9],8]]] +[[5,6],[[[9,9],[4,0]],[7,[2,0]]]] +[[[[5,1],6],[[1,0],[7,1]]],[[6,[1,0]],[[4,2],[0,0]]]] +[[[4,[0,2]],6],[[[4,3],[8,0]],[[9,6],[1,5]]]] +[[[[5,3],[2,2]],[8,[8,3]]],[[9,1],2]] +[[3,4],[[[4,7],[2,3]],[9,[9,0]]]] +[[[5,[6,2]],[[1,5],[9,2]]],[[[7,9],3],[[6,7],[6,2]]]] +[[[5,3],9],[[2,[4,3]],[[5,3],1]]] diff --git a/src/day18.rs b/src/day18.rs new file mode 100644 index 0000000..a4ee87a --- /dev/null +++ b/src/day18.rs @@ -0,0 +1,565 @@ +use std::collections::VecDeque; +use std::fmt::Display; + +use itertools::Itertools; + +pub fn day18() { + assert_eq!( + parse("[[1,2],3]"), + SnailNum { + tokens: vec![ + Token::Start, + Token::Start, + Token::Number(1), + Token::Number(2), + Token::End, + Token::Number(3), + Token::End, + ], + } + ); + let pairs = [ + ("[[[[[9,8],1],2],3],4]", "[[[[0,9],2],3],4]"), + ("[7,[6,[5,[4,[3,2]]]]]", "[7,[6,[5,[7,0]]]]"), + ("[[6,[5,[4,[3,2]]]],1]", "[[6,[5,[7,0]]],3]"), + ( + "[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]", + "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]", + ), + ( + "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]", + "[[3,[2,[8,0]]],[9,[5,[7,0]]]]", + ), + ]; + for (input, expected) in pairs { + let mut num = parse(input); + assert!(num.try_explode()); + assert_eq!(num, parse(expected)); + } + + assert_eq!( + parse("[[[[4,3],4],4],[7,[[8,4],9]]]").add(parse("[1,1]")), + parse("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]") + ); + + // assert_eq!( + // sum_all(&["[1,1]", "[2,2]", "[3,3]", "[4,4]"]), + // parse("[[[[1,1],[2,2]],[3,3]],[4,4]]") + // ); + // assert_eq!( + // sum_all(&["[1,1]", "[2,2]", "[3,3]", "[4,4]", "[5,5]"]), + // parse("[[[[3,0],[5,3]],[4,4]],[5,5]]") + // ); + // assert_eq!( + // sum_all(&["[1,1]", "[2,2]", "[3,3]", "[4,4]", "[5,5]", "[6,6]"]), + // parse("[[[[5,0],[7,4]],[5,5]],[6,6]]") + // ); + + assert_eq!(parse("[[1,2],[[3,4],5]]").magnitude(), 143); + assert_eq!(parse("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]").magnitude(), 1384); + assert_eq!( + parse("[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]").magnitude(), + 3488 + ); + + let input = include_str!("../input/day18.txt"); + let nums = input.trim().lines().map(|l| parse(l.trim())); + let sum = nums.clone().reduce(|a, b| a.add(b)).unwrap(); + println!("part 1: {}", sum.magnitude()); + + let max_sum_magnitude = nums + .clone() + .cartesian_product(nums) + .map(|(a, b)| a.add(b).magnitude()) + .max() + .unwrap(); + println!("part 2: {}", max_sum_magnitude); +} + +#[derive(Debug, PartialEq, Clone)] +struct SnailNum { + tokens: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +enum Token { + Start, + End, + Number(usize), +} + +impl Token { + fn is_num(&self) -> bool { + match self { + Token::Number(_) => true, + _ => false, + } + } + + fn as_num(&self) -> &usize { + match self { + Token::Number(n) => n, + _ => panic!(), + } + } + + fn as_num_mut(&mut self) -> &mut usize { + match self { + Token::Number(n) => n, + _ => panic!(), + } + } +} + +fn parse(s: &str) -> SnailNum { + let mut v = vec![]; + let mut it = s.chars().peekable(); + let mut depth = 0; + while let Some(c) = it.next() { + match c { + '[' => { + depth += 1; + v.push(Token::Start); + } + ']' => { + depth -= 1; + v.push(Token::End); + } + ',' => (), + '0'..='9' => { + let mut n = c.to_digit(10).unwrap() as usize; + while let Some(c @ '0'..='9') = it.peek() { + n *= 10; + n += c.to_digit(10).unwrap() as usize; + it.next(); + } + v.push(Token::Number(n)); + } + _ => panic!(), + } + } + assert_eq!(depth, 0); + SnailNum { tokens: v } +} + +impl Display for SnailNum { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #[derive(PartialEq)] + enum State { + Left, + Right, + } + let mut state = VecDeque::new(); + for (i, t) in self.tokens.iter().enumerate() { + match t { + Token::Start => { + write!(f, "[").expect("write"); + state.push_front(State::Left); + } + Token::End => { + write!(f, "]").expect("write"); + if state.pop_front().unwrap() == State::Left { + if i < self.tokens.len() - 1 { + write!(f, ",").expect("write"); + } + state.push_front(State::Right); + } + } + Token::Number(n) => { + write!(f, "{}", n).expect("write"); + if state.pop_front().unwrap() == State::Left { + write!(f, ",").expect("write"); + state.push_front(State::Right); + } + } + } + } + assert!(state.is_empty()); + Ok(()) + } +} + +impl SnailNum { + fn magnitude(&self) -> usize { + let mut tokens = self.tokens.clone(); + while tokens.len() != 1 { + let mut first_of_two_numbers: Option = None; + for i in (0..tokens.len() - 2).rev() { + if !tokens[i].is_num() || !tokens[i + 1].is_num() { + continue; + } + first_of_two_numbers = Some(i); + break; + } + let i = first_of_two_numbers.unwrap(); + let mag = 3 * *tokens[i].as_num_mut() + 2 * *tokens[i + 1].as_num_mut(); + tokens.splice(i - 1..=i + 2, [Token::Number(mag)]); + } + *tokens.first().unwrap().as_num() + } + + fn add(mut self, mut other: SnailNum) -> SnailNum { + let mut tokens = vec![]; + tokens.push(Token::Start); + tokens.append(&mut self.tokens); + tokens.append(&mut other.tokens); + tokens.push(Token::End); + let mut new = SnailNum { tokens }; + new.reduce(); + new + } + + fn reduce(&mut self) { + loop { + if self.try_explode() { + continue; + } + if self.try_split() { + continue; + } + break; + } + } + + fn try_explode(&mut self) -> bool { + let mut depth = 0; + let mut exploding_start_index: Option = None; + for (i, t) in self.tokens.iter().enumerate() { + if t == &Token::Start { + depth += 1; + if depth > 4 { + exploding_start_index = Some(i); + break; + } + } else if t == &Token::End { + depth -= 1; + } + } + + if let Some(index) = exploding_start_index { + let l = *self.tokens[index + 1].as_num_mut(); + let r = *self.tokens[index + 2].as_num_mut(); + + let mut prev_num_index = Some(index - 1); + let mut next_num_index = Some(index + 4); + loop { + if prev_num_index.is_none() || self.tokens[prev_num_index.unwrap()].is_num() { + break; + } + prev_num_index = prev_num_index.unwrap().checked_sub(1); + } + loop { + if self.tokens[next_num_index.unwrap()].is_num() { + break; + } + let next = next_num_index.unwrap() + 1; + if next >= self.tokens.len() { + next_num_index = None; + break; + } else { + next_num_index = Some(next); + } + } + if let Some(i) = prev_num_index { + *self.tokens[i].as_num_mut() += l; + } + if let Some(i) = next_num_index { + *self.tokens[i].as_num_mut() += r; + } + + self.tokens.splice(index..=index + 3, [Token::Number(0)]); + + true + } else { + false + } + } + + fn try_split(&mut self) -> bool { + let mut index = 0; + loop { + if index >= self.tokens.len() { + return false; + } + if let Token::Number(10..) = self.tokens[index] { + break; + } + index += 1; + } + + let old = *self.tokens[index].as_num_mut(); + let half = (old as f64) / 2_f64; + self.tokens.splice( + index..=index, + [ + Token::Start, + Token::Number(half.floor() as usize), + Token::Number(half.ceil() as usize), + Token::End, + ], + ); + + true + } +} + +// use std::cell::{Cell, Ref, RefCell}; +// use std::collections::VecDeque; +// use std::fmt::Display; +// use std::iter::Peekable; + +// pub fn day18() { +// assert_eq!( +// SnailNum::from("[[1,2],3]"), +// SnailNum { +// left: NumOrSnail::Snail( +// SnailNum { +// left: NumOrSnail::Num(1.into()).into(), +// right: NumOrSnail::Num(2.into()).into() +// } +// .into() +// ) +// .into(), +// right: NumOrSnail::Num(3.into()).into(), +// } +// ); +// assert_eq!( +// SnailNum::from("[1,2]").add(SnailNum::from("[[3,4],5]")), +// SnailNum { +// left: NumOrSnail::Snail( +// SnailNum { +// left: NumOrSnail::Num(1.into()).into(), +// right: NumOrSnail::Num(2.into()).into(), +// } +// .into() +// ) +// .into(), +// right: NumOrSnail::Snail( +// SnailNum { +// left: NumOrSnail::Snail( +// SnailNum { +// left: NumOrSnail::Num(3.into()).into(), +// right: NumOrSnail::Num(4.into()).into(), +// } +// .into() +// ) +// .into(), +// right: NumOrSnail::Num(5.into()).into(), +// } +// .into() +// ) +// .into() +// } +// ); +// let mut sn = SnailNum::from("[[6,[5,[4,[3,2]]]],1]"); +// sn.reduce(); +// println!("{}", sn); +// } + +// fn parse_snail_num(s: &mut Peekable>) -> SnailNum { +// assert_eq!(s.next(), Some('[')); +// let left: NumOrSnail; +// match s.peek() { +// Some('[') => left = NumOrSnail::Snail(Box::new(parse_snail_num(s))), +// Some(_) => left = NumOrSnail::Num(parse_num(s).into()), +// None => panic!(), +// } +// assert_eq!(s.next(), Some(',')); +// let right: NumOrSnail; +// match s.peek() { +// Some('[') => right = NumOrSnail::Snail(Box::new(parse_snail_num(s))), +// Some(_) => right = NumOrSnail::Num(parse_num(s).into()), +// None => panic!(), +// } +// assert_eq!(s.next(), Some(']')); +// SnailNum { +// left: left.into(), +// right: right.into(), +// } +// } + +// fn parse_num(s: &mut Peekable>) -> usize { +// let mut res = 0; +// while let Some('0'..='9') = s.peek() { +// let digit = s.next().unwrap().to_digit(10); +// res *= 10; +// res += digit.unwrap() as usize; +// } +// res +// } + +// #[derive(Debug, PartialEq)] +// struct SnailNum { +// left: RefCell, +// right: RefCell, +// } + +// #[derive(Debug, PartialEq)] +// enum NumOrSnail { +// Num(Cell), +// Snail(Box), +// } + +// #[derive(Debug, Clone, Copy, PartialEq)] +// enum Side { +// Left, +// Right, +// } + +// impl SnailNum { +// fn add(self, other: SnailNum) -> SnailNum { +// SnailNum { +// left: NumOrSnail::Snail(Box::new(self)).into(), +// right: NumOrSnail::Snail(Box::new(other)).into(), +// } +// } + +// fn reduce(&mut self) { +// loop { +// if self.try_explode() { +// continue; +// } else if self.try_split() { +// continue; +// } else { +// break; +// } +// } +// } + +// fn try_explode(&mut self) -> bool { +// let mut queue: VecDeque<(&SnailNum, Vec<(Ref<'_, &SnailNum>, Side)>, usize)> = +// VecDeque::new(); +// queue.push_back((self, vec![], 0)); +// while let Some((item, mut parents, depth)) = queue.pop_front() { +// if depth >= 4 { +// let l = item.left.borrow().as_num(); +// let r = item.right.borrow().as_num(); +// // add item's right to next leaf to the right: +// // go up until we're the left child then go back down to find the leftmost leaf of the right child +// // and similarly for item's left to next leaf to the left +// let mut immediate_parent: Option<(&SnailNum, Side)> = None; +// let mut left_parent: Option<&SnailNum> = None; +// let mut right_parent: Option<&SnailNum> = None; +// while let Some((p, side)) = parents.pop() { +// if immediate_parent.is_none() { +// immediate_parent = Some((p, side)); +// } +// if left_parent.is_none() && side == Side::Left { +// left_parent = Some(p); +// } else if right_parent.is_none() && side == Side::Right { +// right_parent = Some(p); +// } +// if left_parent.is_some() && right_parent.is_some() { +// break; +// } +// } +// if let Some(left_parent) = left_parent { +// left_parent.right.borrow().add_to_leftmost_leaf(r); +// } +// if let Some(right_parent) = right_parent { +// right_parent.left.borrow().add_to_rightmost_leaf(l); +// } +// // replace item with 0 +// match immediate_parent.unwrap().1 { +// Side::Left => { +// *immediate_parent.unwrap().0.left.borrow_mut() = NumOrSnail::Num(0.into()); +// } +// Side::Right => { +// *immediate_parent.unwrap().0.right.borrow_mut() = NumOrSnail::Num(0.into()); +// } +// } +// return true; +// } else { +// // add right first so that left is at the front of the queue +// let right_borrowed = item.right.borrow(); +// if let Some(right) = right_borrowed.as_snail() { +// let mut v = parents.clone(); +// v.push((item, Side::Right)); +// queue.push_front((right, v, depth + 1)); +// } +// // if let NumOrSnail::Snail(left) = &item.left { +// // let mut v = parents.clone(); +// // v.push((item, Side::Left)); +// // queue.push_front((left.as_ref(), v, depth + 1)); +// // } +// } +// } +// false +// } + +// fn add_to_leftmost_leaf(&self, amt: usize) { +// self.left.borrow().add_to_leftmost_leaf(amt); +// } + +// fn add_to_rightmost_leaf(&self, amt: usize) { +// self.right.borrow().add_to_rightmost_leaf(amt); +// } + +// fn try_split(&mut self) -> bool { +// false +// } +// } + +// impl From<&str> for SnailNum { +// fn from(s: &str) -> Self { +// parse_snail_num(&mut s.chars().peekable()) +// } +// } + +// impl Display for SnailNum { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "[").expect("write"); +// write!(f, "{}", self.left.borrow()).expect("write"); +// write!(f, ",").expect("write"); +// write!(f, "{}", self.right.borrow()).expect("write"); +// write!(f, "]").expect("write"); +// Ok(()) +// } +// } + +// impl NumOrSnail { +// fn as_num(&self) -> usize { +// match self { +// NumOrSnail::Num(n) => n.get(), +// _ => panic!(), +// } +// } + +// fn as_snail(&self) -> Option<&SnailNum> { +// match self { +// NumOrSnail::Snail(b) => Some(b), +// _ => None, +// } +// } + +// fn add_to_leftmost_leaf(&self, amt: usize) { +// match self { +// NumOrSnail::Num(ref cur) => { +// cur.set(cur.get() + amt); +// } +// NumOrSnail::Snail(child) => { +// child.add_to_leftmost_leaf(amt); +// } +// } +// } + +// fn add_to_rightmost_leaf(&self, amt: usize) { +// match self { +// NumOrSnail::Num(ref cur) => { +// cur.set(cur.get() + amt); +// } +// NumOrSnail::Snail(child) => { +// child.add_to_rightmost_leaf(amt); +// } +// } +// } +// } + +// impl Display for NumOrSnail { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match self { +// NumOrSnail::Num(n) => write!(f, "{}", n.get()), +// NumOrSnail::Snail(snail) => write!(f, "{}", *snail), +// } +// } +// } diff --git a/src/main.rs b/src/main.rs index 2c2359e..53470f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,8 @@ mod day14; mod day15; mod day16; mod day17; +mod day18; fn main() { - day17::day17(); + day18::day18(); }