566 lines
17 KiB
Rust
566 lines
17 KiB
Rust
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<Token>,
|
|
}
|
|
|
|
#[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<usize> = 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<usize> = 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<impl Iterator<Item = char>>) -> 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<impl Iterator<Item = char>>) -> 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<NumOrSnail>,
|
|
// right: RefCell<NumOrSnail>,
|
|
// }
|
|
|
|
// #[derive(Debug, PartialEq)]
|
|
// enum NumOrSnail {
|
|
// Num(Cell<usize>),
|
|
// Snail(Box<SnailNum>),
|
|
// }
|
|
|
|
// #[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),
|
|
// }
|
|
// }
|
|
// }
|