From 6f9ee71a9bf84b36daf8e55736ba202456258874 Mon Sep 17 00:00:00 2001 From: pjht Date: Wed, 7 Dec 2022 13:13:31 -0600 Subject: [PATCH] Day 7 --- Cargo.toml | 1 + src/day07.rs | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 184 insertions(+) create mode 100644 src/day07.rs diff --git a/Cargo.toml b/Cargo.toml index 04b351e..412739c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ edition = "2021" [dependencies] aoc-runner = "0.3.0" aoc-runner-derive = "0.3.0" +id_tree = "1.8.0" diff --git a/src/day07.rs b/src/day07.rs new file mode 100644 index 0000000..ef71129 --- /dev/null +++ b/src/day07.rs @@ -0,0 +1,182 @@ +use std::{path::PathBuf, str::FromStr}; + +use aoc_runner_derive::{aoc, aoc_generator}; +use id_tree::{InsertBehavior, Node, Tree}; + +#[derive(Clone, Debug, PartialEq, Eq)] +struct File { + name: String, + size: usize, +} + +#[derive(Debug, PartialEq, Eq)] +enum DirectoryEntry { + File(File), + Dir(String), +} + +impl DirectoryEntry { + fn as_file(&self) -> Option<&File> { + if let Self::File(v) = self { + Some(v) + } else { + None + } + } + + fn as_dir(&self) -> Option<&String> { + if let Self::Dir(v) = self { + Some(v) + } else { + None + } + } +} + +#[derive(Copy, Clone, Debug)] +pub struct InvalidDirectoryEntry; + +impl FromStr for DirectoryEntry { + type Err = InvalidDirectoryEntry; + + fn from_str(s: &str) -> Result { + let (a, b) = s.split_once(' ').ok_or(InvalidDirectoryEntry)?; + if a == "dir" { + Ok(Self::Dir(b.to_string())) + } else { + Ok(Self::File(File { + name: b.to_string(), + size: a.parse().map_err(|_| InvalidDirectoryEntry)?, + })) + } + } +} + +#[derive(Debug, PartialEq, Eq)] +enum CliOperation { + Cd(String), + Ls(Vec), +} + +impl CliOperation { + fn as_ls(&self) -> Option<&Vec> { + if let Self::Ls(v) = self { + Some(v) + } else { + None + } + } +} + +#[aoc_generator(day7)] +fn input_generator(input: &str) -> Vec { + let mut lines = input.lines().peekable(); + let mut ops = Vec::new(); + while let Some(line) = lines.next() { + let line = line.strip_prefix("$ ").unwrap(); + if line.contains(' ') { + ops.push(CliOperation::Cd( + line.strip_prefix("cd ").unwrap().to_string(), + )); + } else { + let mut entries = Vec::new(); + while let Some(line) = lines.next_if(|line| !line.starts_with('$')) { + entries.push(line.parse().unwrap()); + } + ops.push(CliOperation::Ls(entries)); + } + } + ops +} + +fn compute_dir_sizes(input: &[CliOperation]) -> Tree<(usize, usize)> { + if input[0] != CliOperation::Cd("/".to_string()) { + panic!(); + } + let mut tree = Tree::new(); + let mut current_node = tree + .insert( + Node::new(( + input[1] + .as_ls() + .unwrap() + .iter() + .filter_map(DirectoryEntry::as_file) + .map(|file| file.size) + .sum::(), + 0, + )), + InsertBehavior::AsRoot, + ) + .unwrap(); + for op in input { + match op { + CliOperation::Cd(dir) => { + if dir == ".." { + current_node = tree + .ancestor_ids(¤t_node) + .unwrap() + .next() + .unwrap() + .clone(); + } else { + current_node = tree + .insert(Node::new((0, 0)), InsertBehavior::UnderNode(¤t_node)) + .unwrap(); + }; + } + CliOperation::Ls(entries) => { + *tree.get_mut(¤t_node).unwrap().data_mut() = ( + entries + .iter() + .filter_map(DirectoryEntry::as_file) + .map(|file| file.size) + .sum::(), + 0, + ) + } + } + } + let post_order_ids = tree + .traverse_post_order_ids(tree.root_node_id().unwrap()) + .unwrap() + .collect::>(); + for id in post_order_ids { + let child_sum = tree + .children(&id) + .unwrap() + .map(|node| node.data().1) + .sum::(); + let self_sum = tree.get(&id).unwrap().data().0; + tree.get_mut(&id).unwrap().data_mut().1 = child_sum + self_sum; + } + tree +} + +#[aoc(day7, part1)] +fn solve_part1(input: &[CliOperation]) -> usize { + let tree = compute_dir_sizes(input); + let mut sum = 0; + for node in tree + .traverse_post_order(tree.root_node_id().unwrap()) + .unwrap() + { + if node.data().1 <= 100000 { + sum += node.data().1; + } + } + sum +} + +#[aoc(day7, part2)] +fn solve_part2(input: &[CliOperation]) -> usize { + let tree = compute_dir_sizes(input); + let free_space = 70_000_000 - tree.get(tree.root_node_id().unwrap()).unwrap().data().1; + let necessary_to_free = 30_000_000 - free_space; + tree.traverse_post_order(tree.root_node_id().unwrap()) + .unwrap() + .filter(|node| node.data().1 >= necessary_to_free) + .map(|node| node.data().1) + .min() + .unwrap() +} diff --git a/src/lib.rs b/src/lib.rs index 2eb43e3..76d945b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ mod day03; mod day04; mod day05; mod day06; +mod day07; aoc_lib! { year = 2022 }