completely rework day 18

This commit is contained in:
Joseph Montanaro 2022-01-07 14:57:22 -08:00
parent 54053779aa
commit 61374b8655

View File

@ -1,90 +1,212 @@
// use std::iter::Peekable;
use std::str::Chars;
use color_eyre::eyre; use color_eyre::eyre;
use eyre::eyre;
#[derive(Debug)] #[derive(Debug)]
enum Item { enum Node {
Value(u8), Leaf(u8),
SubPair(Box<Pair>), Branch {
} left: Box<Node>,
right: Box<Node>,
impl Item {
fn magnitude(&self) -> usize {
match self {
Item::Value(v) => *v as usize,
Item::SubPair(pair) => pair.magnitude(),
}
} }
} }
#[derive(Debug)] impl Node {
struct Pair { fn is_leaf(&self) -> bool {
left: Item, match self {
right: Item, Node::Leaf(_) => true,
} Node::Branch {..} => false,
}
}
fn is_branch(&self) -> bool {
match self {
Node::Leaf(_) => false,
Node::Branch {..} => true,
}
}
fn unwrap_value(&self) -> u8 {
match self {
Node::Leaf(v) => *v,
Node::Branch {..} => panic!("Attempted to unwrap value from a branch node"),
}
}
fn value_mut(&mut self) -> &mut u8 {
match self {
Node::Leaf(v) => v,
Node::Branch {..} => panic!("Attempted to unwrap value from a branch 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(),
}
}
impl Pair {
fn magnitude(&self) -> usize { fn magnitude(&self) -> usize {
3 * self.left.magnitude() + 2 * self.right.magnitude() match self {
Node::Leaf(v) => *v as usize,
Node::Branch {left, right} => 3 * left.magnitude() + 2 * right.magnitude(),
}
} }
} }
struct Parser<'a> { struct Parser<'a> {
chars: Chars<'a>, chars: std::str::Chars<'a>,
} }
impl<'a> Parser<'a> { impl<'a> Parser<'a> {
fn from(src: &'a str) -> Parser<'a> { fn from(src: &'a str) -> Parser<'a> {
let mut chars = src.chars(); let mut chars = src.chars();
chars.next(); // throw away the opening bracket so we don't nest too deeply chars.next(); // throw away the opening bracket to avoid nesting too deeply
Parser {chars} Parser {chars}
} }
fn parse(&mut self) -> eyre::Result<Pair> { fn parse(&mut self) -> Node {
let mut items = Vec::with_capacity(2); let mut left = None;
while items.len() < 2 { loop {
let item = match self.chars.next() { let node = match self.chars.next() {
Some('[') => Item::SubPair(Box::new(self.parse()?)), Some('[') => self.parse(),
Some(','|']') => {continue;}, Some(','|']') => {continue;},
Some(c) => Item::Value(c.to_digit(10).unwrap() as u8), Some(c) => Node::Leaf(c.to_digit(10).unwrap() as u8),
None => {return Err(eyre!("Ran out of characters to parse"));}, None => {dbg!(left); panic!("Ran out of characters while parsing");},
}; };
items.push(item);
}
let right = items.pop().unwrap(); if left.is_none() {
let left = items.pop().unwrap(); left = Some(node);
Ok(Pair {left, right}) }
else {
return Node::Branch {
left: Box::new(left.unwrap()),
right: Box::new(node),
};
}
}
} }
} }
fn load(data: &str) -> Vec<Node> {
fn load(data: &str) -> eyre::Result<Vec<Pair>>{ let mut nodes = Vec::new();
let mut pairs = Vec::new();
for line in data.lines() { for line in data.lines() {
let mut parser = Parser::from(line); let mut parser = Parser::from(line);
let pair = parser.parse() nodes.push(parser.parse());
.map_err(|_| eyre!("Invalid input: {}", line))?;
pairs.push(pair);
} }
Ok(pairs) nodes
} }
const _SAMPLE: &str = "[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]"; #[derive(Debug)]
enum ReduceOp {
Noop,
Split,
Explode(u8, u8),
}
pub fn run(data: &str) -> eyre::Result<(usize, usize)> {
// let pairs = load(data)?;
// dbg!(pairs.len());
// dbg!(&pairs[0]);
let mut parser = Parser::from(_SAMPLE); fn walk(node: &mut Node, depth: usize) -> ReduceOp {
let root = parser.parse()?; // just to improve readability
dbg!(root.magnitude()); use {Node::*, ReduceOp::*};
if depth >= 4 {
if let Branch {left, right} = node {
let v1 = left.unwrap_value();
let v2 = right.unwrap_value();
*node = Leaf(0);
return Explode(v1, v2);
}
}
match node {
Leaf(v) if *v > 9 => {
*node = Branch {
left: Box::new(Leaf(*v / 2)),
right: Box::new(Leaf((*v + 1) / 2)),
};
return Split
},
Leaf(_) => return Noop,
// here's where it gets complicated
Branch {left, right} => {
// keep walking, depth-first and left-first
let res = walk(left, depth + 1);
// if a sub-node exploded, and our right node is a leaf, add the right half of the explode to our right leaf
match res {
Explode(v1, v2) if v2 > 0 => {
*right.leftmost_value_mut() += v2;
// and propagate the explode up again, except we've already used v2 so replace it with 0
return Explode(v1, 0);
},
Explode(..)|Split => return res,
Noop => (),
}
// if no children of the left branch exploded, keep going with the right
let res = walk(right, depth + 1);
// same concept, except we're adding the left half of the sub-explode to our left leaf (if it is a leaf)
match res {
Explode(v1, v2) if v1 > 0 => {
*left.rightmost_value_mut() += v1;
return Explode(0, v2);
},
Explode(..)|Split => return res,
Noop => (),
}
}
}
// if everything else completes, then there were no splits or explodes below us in the tree
return Noop
}
fn reduce(node: &mut Node) {
while let ReduceOp::Explode(..)|ReduceOp::Split = walk(node, 0) {}
}
fn add(left: Node, right: Node) -> Node {
let mut sum = Node::Branch {
left: Box::new(left),
right: Box::new(right),
};
reduce(&mut sum);
sum
}
const SAMPLE: &str = "[[1,2],[[3,4],5]]
[[[[0,7],4],[[7,8],[6,0]]],[8,1]]
[[[[1,1],[2,2]],[3,3]],[4,4]]
[[[[3,0],[5,3]],[4,4]],[5,5]]
[[[[5,0],[7,4]],[5,5]],[6,6]]
[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]";
pub fn run(_data: &str) -> eyre::Result<(usize, usize)> {
let nodes = load(SAMPLE);
// let one = nodes.into_iter().reduce(|sum, next| add(sum, next));
// dbg!(one);
for node in nodes {
println!("{}", node.magnitude());
}
Ok((0, 0)) Ok((0, 0))
} }