This commit is contained in:
Shadowfacts 2021-12-20 19:09:10 -05:00
parent 260f0cd6c3
commit a8509f694a
3 changed files with 667 additions and 1 deletions

100
input/day18.txt Normal file
View File

@ -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]]]

565
src/day18.rs Normal file
View File

@ -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<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),
// }
// }
// }

View File

@ -16,7 +16,8 @@ mod day14;
mod day15; mod day15;
mod day16; mod day16;
mod day17; mod day17;
mod day18;
fn main() { fn main() {
day17::day17(); day18::day18();
} }