Add (messy) day 18 part 1 solution
parent
ef54d76d28
commit
361a0004b4
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug executable 'day18'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"build",
|
||||
"--bin=day18",
|
||||
"--package=day18"
|
||||
],
|
||||
"filter": {
|
||||
"name": "day18",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
"sample9.txt"
|
||||
],
|
||||
"cwd": "${workspaceFolder}"
|
||||
},
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "Debug unit tests in executable 'day18'",
|
||||
"cargo": {
|
||||
"args": [
|
||||
"test",
|
||||
"--no-run",
|
||||
"--bin=day18",
|
||||
"--package=day18"
|
||||
],
|
||||
"filter": {
|
||||
"name": "day18",
|
||||
"kind": "bin"
|
||||
}
|
||||
},
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "day18"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "day18"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nom = "7.1"
|
||||
petgraph = "0.6"
|
|
@ -0,0 +1,541 @@
|
|||
#![warn(clippy::all, clippy::pedantic)]
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::Formatter;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::mem;
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
character::complete::char,
|
||||
character::complete::digit1,
|
||||
combinator::{eof, map_res},
|
||||
sequence::{delimited, separated_pair, terminated},
|
||||
IResult,
|
||||
};
|
||||
use petgraph::dot::{Config, Dot};
|
||||
use petgraph::graph::NodeIndex;
|
||||
use petgraph::stable_graph::StableDiGraph;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum InputPair {
|
||||
Pair(Box<InputPair>, Box<InputPair>),
|
||||
Leaf(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum PairNode {
|
||||
PairRoot,
|
||||
Leaf(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum Direction {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum EdgeType {
|
||||
Parent,
|
||||
Child(Direction),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ProblemTree {
|
||||
graph: StableDiGraph<PairNode, EdgeType>,
|
||||
root_idx: NodeIndex,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn get_other(self) -> Self {
|
||||
match self {
|
||||
Self::Left => Self::Right,
|
||||
Self::Right => Self::Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ProblemTree {
|
||||
fn build(left: &InputPair, right: &InputPair) -> Self {
|
||||
let mut graph = StableDiGraph::new();
|
||||
let root_idx = graph.add_node(PairNode::PairRoot);
|
||||
let mut tree = ProblemTree { graph, root_idx };
|
||||
tree.insert_input_pair(left, Direction::Left, root_idx);
|
||||
tree.insert_input_pair(right, Direction::Right, root_idx);
|
||||
|
||||
tree
|
||||
}
|
||||
|
||||
fn insert_input_pair(&mut self, pair: &InputPair, direction: Direction, parent_idx: NodeIndex) {
|
||||
match pair {
|
||||
&InputPair::Leaf(n) => {
|
||||
self.insert_leaf(n, direction, parent_idx);
|
||||
}
|
||||
InputPair::Pair(left, right) => {
|
||||
let root_idx = self.graph.add_node(PairNode::PairRoot);
|
||||
// We're inserting twice into the directional graph so we can differentiate between parent/child relationships
|
||||
self.graph
|
||||
.add_edge(parent_idx, root_idx, EdgeType::Child(direction));
|
||||
self.graph.add_edge(root_idx, parent_idx, EdgeType::Parent);
|
||||
self.insert_input_pair(left, Direction::Left, root_idx);
|
||||
self.insert_input_pair(right, Direction::Right, root_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// inserts a pair to the tree that will be a sibling to the root, with a new root being planted in this tree
|
||||
fn insert_root_sibling_input_pair(&mut self, pair: &InputPair, direction: Direction) {
|
||||
let new_root_idx = self.graph.add_node(PairNode::PairRoot);
|
||||
let old_root_idx = self.root_idx;
|
||||
self.root_idx = new_root_idx;
|
||||
self.graph.add_edge(
|
||||
new_root_idx,
|
||||
old_root_idx,
|
||||
EdgeType::Child(direction.get_other()),
|
||||
);
|
||||
self.graph
|
||||
.add_edge(old_root_idx, new_root_idx, EdgeType::Parent);
|
||||
|
||||
self.insert_input_pair(pair, direction, new_root_idx);
|
||||
}
|
||||
|
||||
fn insert_leaf(&mut self, value: u32, direction: Direction, parent_idx: NodeIndex) {
|
||||
let leaf_idx = self.graph.add_node(PairNode::Leaf(value));
|
||||
// We're inserting twice into the directional graph so we can differentiate between parent/child relationships
|
||||
self.graph
|
||||
.add_edge(parent_idx, leaf_idx, EdgeType::Child(direction));
|
||||
self.graph.add_edge(leaf_idx, parent_idx, EdgeType::Parent);
|
||||
}
|
||||
|
||||
fn magnitude(&mut self) -> u32 {
|
||||
let mut to_visit = vec![(1, self.root_idx)];
|
||||
let mut total = 0;
|
||||
while let Some((n, visiting_idx)) = to_visit.pop() {
|
||||
let visiting = self.graph.node_weight(visiting_idx).unwrap();
|
||||
let neighbors = self.graph.neighbors(visiting_idx);
|
||||
|
||||
match visiting {
|
||||
PairNode::PairRoot => {
|
||||
for neighbor in neighbors {
|
||||
let edge_idx = self.graph.find_edge(visiting_idx, neighbor).unwrap();
|
||||
let edge_type = self.graph.edge_weight(edge_idx).unwrap();
|
||||
match edge_type {
|
||||
EdgeType::Child(Direction::Left) => to_visit.push((3 * n, neighbor)),
|
||||
EdgeType::Child(Direction::Right) => to_visit.push((2 * n, neighbor)),
|
||||
EdgeType::Parent => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
PairNode::Leaf(visiting_value) => {
|
||||
total += n * visiting_value;
|
||||
// for neighbor in neighbors {
|
||||
// let edge_idx = self.graph.find_edge(visiting_idx, neighbor).unwrap();
|
||||
// let edge_type = self.graph.edge_weight(edge_idx).unwrap();
|
||||
// }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
total
|
||||
}
|
||||
|
||||
fn reduce(&mut self) {
|
||||
let mut performed_action: Option<bool> = None;
|
||||
while performed_action.unwrap_or(true) {
|
||||
performed_action = Some(false);
|
||||
let explode_candidate = self.find_node_to_explode();
|
||||
let split_candidate = self.find_node_to_split();
|
||||
|
||||
if let Some(to_explode) = explode_candidate {
|
||||
performed_action = Some(true);
|
||||
self.explode_in_relative_direction(to_explode, Direction::Left);
|
||||
self.explode_in_relative_direction(to_explode, Direction::Right);
|
||||
|
||||
let neighbors = self.graph.neighbors(to_explode).collect::<Vec<_>>();
|
||||
for neighbor in neighbors {
|
||||
if let PairNode::Leaf(_) = self.graph.node_weight(neighbor).unwrap() {
|
||||
self.graph.remove_node(dbg!(neighbor));
|
||||
}
|
||||
}
|
||||
|
||||
let to_explode_weight = self.graph.node_weight_mut(to_explode).unwrap();
|
||||
*to_explode_weight = PairNode::Leaf(0);
|
||||
// print_tree(self);
|
||||
dbg!(&self);
|
||||
// std::io::stdin().read_line(&mut String::new());
|
||||
}
|
||||
|
||||
if performed_action.unwrap_or(false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(to_split) = split_candidate {
|
||||
performed_action = Some(true);
|
||||
self.split_node(to_split);
|
||||
|
||||
dbg!(&self);
|
||||
// std::io::stdin().read_line(&mut String::new());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_node_to_explode(&self) -> Option<NodeIndex> {
|
||||
self.find_node_to_reduce_below_or_at(self.root_idx, 0, |node, depth| {
|
||||
depth >= 4 && matches!(node, PairNode::PairRoot)
|
||||
})
|
||||
}
|
||||
|
||||
fn find_node_to_split(&self) -> Option<NodeIndex> {
|
||||
self.find_node_to_reduce_below_or_at(self.root_idx, 0, |node, _| {
|
||||
if let PairNode::Leaf(n) = node {
|
||||
n >= 10
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn find_node_to_reduce_below_or_at<F>(
|
||||
&self,
|
||||
below_idx: NodeIndex,
|
||||
node_depth: usize,
|
||||
criteria: F,
|
||||
) -> Option<NodeIndex>
|
||||
where
|
||||
// Takes the node itself and its depth
|
||||
F: Copy + Fn(PairNode, usize) -> bool,
|
||||
{
|
||||
let find_from_child = |child_idx| {
|
||||
let node = self
|
||||
.graph
|
||||
.node_weight(child_idx)
|
||||
.expect("got a child that didn't exist in the graph");
|
||||
|
||||
// if let PairNode::Leaf(_) = node {
|
||||
// return None;
|
||||
// }
|
||||
|
||||
let next_depth = node_depth + 1;
|
||||
// println!(
|
||||
// "{:?} (id={:?}) @ {} => {}",
|
||||
// node, child_idx, node_depth, next_depth
|
||||
// );
|
||||
|
||||
self.find_node_to_reduce_below_or_at(child_idx, next_depth, criteria)
|
||||
};
|
||||
|
||||
let below_type = self.graph.node_weight(below_idx).unwrap();
|
||||
let left_child = self.get_child(below_idx, Direction::Left);
|
||||
let right_child = self.get_child(below_idx, Direction::Right);
|
||||
let left_candidate = left_child.and_then(find_from_child);
|
||||
let right_candidate = right_child.and_then(find_from_child);
|
||||
|
||||
if left_candidate.is_some() {
|
||||
// dbg!(self.graph.node_weight(
|
||||
// self.get_child(left_candidate.unwrap(), Direction::Left)
|
||||
// .unwrap()
|
||||
// ));
|
||||
|
||||
left_candidate
|
||||
} else if right_candidate.is_some() {
|
||||
right_candidate
|
||||
} else if criteria(*below_type, node_depth) {
|
||||
Some(below_idx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parent(&self, node_idx: NodeIndex) -> Option<NodeIndex> {
|
||||
// There must be zero or one by construction of the graph
|
||||
self.graph.neighbors(node_idx).find(|&neighbor_idx| {
|
||||
// This must exist by the fact that we've been returned a neighbor
|
||||
let edge = self.graph.find_edge(node_idx, neighbor_idx).unwrap();
|
||||
let edge_type = self.graph.edge_weight(edge).unwrap();
|
||||
|
||||
matches!(edge_type, EdgeType::Parent)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_child(&self, node_idx: NodeIndex, direction: Direction) -> Option<NodeIndex> {
|
||||
// There should only be one or zero in the iterator, by the construction of the graph.
|
||||
self.graph.neighbors(node_idx).find(|&neighbor_idx| {
|
||||
// This must exist by the fact that we've been returned a neighbor
|
||||
let edge = self.graph.find_edge(node_idx, neighbor_idx).unwrap();
|
||||
let edge_type = self.graph.edge_weight(edge).unwrap();
|
||||
if let EdgeType::Child(child_direction) = edge_type {
|
||||
mem::discriminant(child_direction) == mem::discriminant(&direction)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn explode_in_relative_direction(
|
||||
&mut self,
|
||||
to_explode: NodeIndex,
|
||||
direction: Direction,
|
||||
) -> bool {
|
||||
let explode_value = self
|
||||
.graph
|
||||
.node_weight(self.get_child(to_explode, direction).unwrap())
|
||||
.map(|node| {
|
||||
if let PairNode::Leaf(n) = node {
|
||||
*n
|
||||
} else {
|
||||
// TODO: Probably shouldn't panic but I don't want to make errors right now
|
||||
panic!("got a non-leaf when looking for explode node");
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("Exploding: {:?} (id={:?})", explode_value, to_explode);
|
||||
|
||||
let relative_direction_node_candidate =
|
||||
self.get_leaf_in_relative_direction(to_explode, direction);
|
||||
|
||||
if let Some(relative_direction_node_idx) = relative_direction_node_candidate {
|
||||
if relative_direction_node_idx == to_explode {
|
||||
return false;
|
||||
}
|
||||
|
||||
let relative_direction_node = self
|
||||
.graph
|
||||
.node_weight_mut(relative_direction_node_idx)
|
||||
.unwrap();
|
||||
// println!("{:?} {:?}", relative_direction_node_idx, to_explode);
|
||||
if let PairNode::Leaf(n) = relative_direction_node {
|
||||
println!(
|
||||
"{:?}: {} (id={:?})",
|
||||
direction, *n, relative_direction_node_idx
|
||||
);
|
||||
*n += explode_value;
|
||||
} else {
|
||||
panic!("got a non-leaf from directional leaf lookup");
|
||||
}
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn split_node(&mut self, node_idx: NodeIndex) {
|
||||
// TODO: Probably shouldn't panic but I don't want to write errors right now
|
||||
let node = self.graph.node_weight_mut(node_idx).unwrap();
|
||||
let n = if let PairNode::Leaf(n) = node {
|
||||
*n
|
||||
} else {
|
||||
// TODO: _definitely_ shouldn't panic but I don't want to write errors right now
|
||||
panic!("cannot explode non-leaf")
|
||||
};
|
||||
|
||||
let (left_value, right_value) = get_split_values(n);
|
||||
*node = PairNode::PairRoot;
|
||||
self.insert_leaf(left_value, Direction::Left, node_idx);
|
||||
self.insert_leaf(right_value, Direction::Right, node_idx);
|
||||
}
|
||||
|
||||
fn get_leaf_in_relative_direction(
|
||||
&self,
|
||||
node_idx: NodeIndex,
|
||||
direction: Direction,
|
||||
) -> Option<NodeIndex> {
|
||||
let mut prev_cursor = node_idx;
|
||||
let mut cursor = self.get_parent(node_idx)?;
|
||||
let have_found_root_like_node = |prev_cursor, cursor| {
|
||||
let cursor_left_child = self.get_child(cursor, Direction::Left).unwrap();
|
||||
let cursor_right_child = self.get_child(cursor, Direction::Right).unwrap();
|
||||
let cursor_left_child_node = self.graph.node_weight(cursor_left_child).unwrap();
|
||||
let cursor_right_child_node = self.graph.node_weight(cursor_right_child).unwrap();
|
||||
|
||||
match direction {
|
||||
Direction::Right => {
|
||||
if cursor_right_child == prev_cursor {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Direction::Left => {
|
||||
if cursor_left_child == prev_cursor {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches!(cursor_left_child_node, PairNode::PairRoot)
|
||||
&& matches!(cursor_right_child_node, PairNode::PairRoot)
|
||||
};
|
||||
|
||||
while !have_found_root_like_node(prev_cursor, cursor) {
|
||||
if let Some(directional_child) = self.get_child(cursor, direction) {
|
||||
let directional_child_node = self.graph.node_weight(directional_child).unwrap();
|
||||
if directional_child != cursor
|
||||
&& matches!(directional_child_node, PairNode::Leaf(_))
|
||||
{
|
||||
return Some(directional_child);
|
||||
}
|
||||
}
|
||||
|
||||
prev_cursor = cursor;
|
||||
cursor = self.get_parent(cursor)?;
|
||||
}
|
||||
|
||||
// once we hit the "root", we need to start going downwards by one level, and descend as far as possible
|
||||
// in the opposite direction.
|
||||
let flip_around_node = self.get_child(cursor, direction)?;
|
||||
if flip_around_node == prev_cursor {
|
||||
return None;
|
||||
}
|
||||
|
||||
cursor = flip_around_node;
|
||||
loop {
|
||||
let child_candidate = self.get_child(cursor, direction.get_other());
|
||||
if let Some(child) = child_candidate {
|
||||
cursor = child;
|
||||
} else {
|
||||
return Some(cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn debug_tree(&self, formatter: &mut Formatter<'_>, node_idx: NodeIndex) -> fmt::Result {
|
||||
let node_candidate = self.graph.node_weight(node_idx);
|
||||
if node_candidate.is_none() {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
let node = node_candidate.unwrap();
|
||||
match node {
|
||||
PairNode::Leaf(n) => write!(formatter, "{}", n)?,
|
||||
PairNode::PairRoot => {
|
||||
let left_node_idx_candidate = self.get_child(node_idx, Direction::Left);
|
||||
let right_node_idx_candidate = self.get_child(node_idx, Direction::Right);
|
||||
if left_node_idx_candidate.is_none() || left_node_idx_candidate.is_none() {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
let left_node_idx = left_node_idx_candidate.unwrap();
|
||||
let right_node_idx = right_node_idx_candidate.unwrap();
|
||||
write!(formatter, "[")?;
|
||||
self.debug_tree(formatter, left_node_idx)?;
|
||||
write!(formatter, ",")?;
|
||||
self.debug_tree(formatter, right_node_idx)?;
|
||||
write!(formatter, "]")?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ProblemTree {
|
||||
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
|
||||
self.debug_tree(formatter, self.root_idx)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_split_values(n: u32) -> (u32, u32) {
|
||||
let left = n / 2;
|
||||
let right = if n % 2 == 0 { n / 2 } else { n / 2 + 1 };
|
||||
|
||||
println!("Splitting: {} => {}, {}", n, left, right);
|
||||
|
||||
(left, right)
|
||||
}
|
||||
|
||||
fn parse_snailfish_problem_leaf(chunk: &str) -> IResult<&str, InputPair> {
|
||||
let (remaining, n) = map_res(digit1, str::parse)(chunk)?;
|
||||
Ok((remaining, InputPair::Leaf(n)))
|
||||
}
|
||||
|
||||
fn parse_snailfish_problem_pair(chunk: &str) -> IResult<&str, InputPair> {
|
||||
let (remaining, (pair1, pair2)) = delimited(
|
||||
char('['),
|
||||
separated_pair(
|
||||
alt((parse_snailfish_problem_pair, parse_snailfish_problem_leaf)),
|
||||
char(','),
|
||||
alt((parse_snailfish_problem_pair, parse_snailfish_problem_leaf)),
|
||||
),
|
||||
char(']'),
|
||||
)(chunk)?;
|
||||
|
||||
let res_pair = InputPair::Pair(Box::new(pair1), Box::new(pair2));
|
||||
|
||||
Ok((remaining, res_pair))
|
||||
}
|
||||
|
||||
fn parse_snailfish_problem(input: &str) -> IResult<&str, InputPair> {
|
||||
terminated(parse_snailfish_problem_pair, eof)(input)
|
||||
}
|
||||
|
||||
fn part1(input_pairs: &[InputPair]) -> u32 {
|
||||
let mut problem_tree = if let InputPair::Pair(left, right) = &input_pairs[0] {
|
||||
ProblemTree::build(&*left, &*right)
|
||||
} else {
|
||||
panic!("input pair should be a pair");
|
||||
};
|
||||
|
||||
problem_tree.reduce();
|
||||
|
||||
for input_pair in &input_pairs[1..] {
|
||||
problem_tree.insert_root_sibling_input_pair(input_pair, Direction::Right);
|
||||
problem_tree.reduce();
|
||||
dbg!(&problem_tree);
|
||||
// print_tree(&problem_tree);
|
||||
// std::io::stdin().read_line(&mut String::new());
|
||||
}
|
||||
// print_tree(&problem_tree);
|
||||
dbg!(&problem_tree);
|
||||
|
||||
problem_tree.magnitude()
|
||||
}
|
||||
|
||||
fn print_tree(problem_tree: &ProblemTree) {
|
||||
println!("{:?}", Dot::with_config(&problem_tree.graph, &[]));
|
||||
// let mut to_visit = vec![(0, problem_tree.root_idx)];
|
||||
// while let Some((indentation, idx)) = to_visit.pop() {
|
||||
// let node = problem_tree.graph.node_weight(idx).unwrap();
|
||||
// match node {
|
||||
// PairNode::Leaf(n) => println!(
|
||||
// "{}{}",
|
||||
// (0..=indentation).map(|_| " ").collect::<String>(),
|
||||
// n
|
||||
// ),
|
||||
// PairNode::PairRoot => {
|
||||
// for neighbor in problem_tree.graph.neighbors(idx) {
|
||||
// let edge_idx = problem_tree.graph.find_edge(idx, neighbor).unwrap();
|
||||
// let edge_type = problem_tree.graph.edge_weight(edge_idx).unwrap();
|
||||
// if let EdgeType::Child(_) = edge_type {
|
||||
// to_visit.push((indentation + 1, neighbor));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let input_file_name = env::args().nth(1).expect("No input filename specified");
|
||||
let input_file = File::open(input_file_name).expect("Could not open input file");
|
||||
let all_pairs = BufReader::new(input_file)
|
||||
.lines()
|
||||
.map(|res| res.expect("Failed to read line"))
|
||||
.filter_map(|line| {
|
||||
if line.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (_, input_pair) =
|
||||
parse_snailfish_problem(&line).expect("Failed to parse input line");
|
||||
Some(input_pair)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// .explode(|left, right| InputPair::Pair(Box::new(left), Box::new(right)))
|
||||
// .expect("did not find input problem");
|
||||
|
||||
println!("Part 1: {}", part1(&all_pairs));
|
||||
}
|
Loading…
Reference in New Issue