advent/2021/src/day18.rs
2022-01-08 19:50:46 -08:00

193 lines
4.5 KiB
Rust

use color_eyre::eyre;
#[derive(Debug, Clone)]
enum Node {
Leaf(u8),
Branch {
left: Box<Node>,
right: Box<Node>,
}
}
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<Node> {
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))
}