completely rework day 18
This commit is contained in:
parent
54053779aa
commit
61374b8655
@ -1,90 +1,212 @@
|
||||
// use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
use color_eyre::eyre;
|
||||
use eyre::eyre;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Item {
|
||||
Value(u8),
|
||||
SubPair(Box<Pair>),
|
||||
enum Node {
|
||||
Leaf(u8),
|
||||
Branch {
|
||||
left: Box<Node>,
|
||||
right: Box<Node>,
|
||||
}
|
||||
}
|
||||
|
||||
impl Item {
|
||||
impl Node {
|
||||
fn is_leaf(&self) -> bool {
|
||||
match self {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
fn magnitude(&self) -> usize {
|
||||
match self {
|
||||
Item::Value(v) => *v as usize,
|
||||
Item::SubPair(pair) => pair.magnitude(),
|
||||
Node::Leaf(v) => *v as usize,
|
||||
Node::Branch {left, right} => 3 * left.magnitude() + 2 * right.magnitude(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Pair {
|
||||
left: Item,
|
||||
right: Item,
|
||||
}
|
||||
|
||||
impl Pair {
|
||||
fn magnitude(&self) -> usize {
|
||||
3 * self.left.magnitude() + 2 * self.right.magnitude()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Parser<'a> {
|
||||
chars: Chars<'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 so we don't nest too deeply
|
||||
chars.next(); // throw away the opening bracket to avoid nesting too deeply
|
||||
Parser {chars}
|
||||
}
|
||||
|
||||
fn parse(&mut self) -> eyre::Result<Pair> {
|
||||
let mut items = Vec::with_capacity(2);
|
||||
while items.len() < 2 {
|
||||
let item = match self.chars.next() {
|
||||
Some('[') => Item::SubPair(Box::new(self.parse()?)),
|
||||
fn parse(&mut self) -> Node {
|
||||
let mut left = None;
|
||||
loop {
|
||||
let node = match self.chars.next() {
|
||||
Some('[') => self.parse(),
|
||||
Some(','|']') => {continue;},
|
||||
Some(c) => Item::Value(c.to_digit(10).unwrap() as u8),
|
||||
None => {return Err(eyre!("Ran out of characters to parse"));},
|
||||
Some(c) => Node::Leaf(c.to_digit(10).unwrap() as u8),
|
||||
None => {dbg!(left); panic!("Ran out of characters while parsing");},
|
||||
};
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
let right = items.pop().unwrap();
|
||||
let left = items.pop().unwrap();
|
||||
Ok(Pair {left, right})
|
||||
if left.is_none() {
|
||||
left = Some(node);
|
||||
}
|
||||
else {
|
||||
return Node::Branch {
|
||||
left: Box::new(left.unwrap()),
|
||||
right: Box::new(node),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn load(data: &str) -> eyre::Result<Vec<Pair>>{
|
||||
let mut pairs = Vec::new();
|
||||
fn load(data: &str) -> Vec<Node> {
|
||||
let mut nodes = Vec::new();
|
||||
for line in data.lines() {
|
||||
let mut parser = Parser::from(line);
|
||||
let pair = parser.parse()
|
||||
.map_err(|_| eyre!("Invalid input: {}", line))?;
|
||||
pairs.push(pair);
|
||||
nodes.push(parser.parse());
|
||||
}
|
||||
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);
|
||||
let root = parser.parse()?;
|
||||
dbg!(root.magnitude());
|
||||
fn walk(node: &mut Node, depth: usize) -> ReduceOp {
|
||||
// just to improve readability
|
||||
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))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user