This commit is contained in:
pjht 2022-12-07 13:13:31 -06:00
parent d35e9844e0
commit 6f9ee71a9b
3 changed files with 184 additions and 0 deletions

View File

@ -8,3 +8,4 @@ edition = "2021"
[dependencies]
aoc-runner = "0.3.0"
aoc-runner-derive = "0.3.0"
id_tree = "1.8.0"

182
src/day07.rs Normal file
View File

@ -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<Self, Self::Err> {
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<DirectoryEntry>),
}
impl CliOperation {
fn as_ls(&self) -> Option<&Vec<DirectoryEntry>> {
if let Self::Ls(v) = self {
Some(v)
} else {
None
}
}
}
#[aoc_generator(day7)]
fn input_generator(input: &str) -> Vec<CliOperation> {
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::<usize>(),
0,
)),
InsertBehavior::AsRoot,
)
.unwrap();
for op in input {
match op {
CliOperation::Cd(dir) => {
if dir == ".." {
current_node = tree
.ancestor_ids(&current_node)
.unwrap()
.next()
.unwrap()
.clone();
} else {
current_node = tree
.insert(Node::new((0, 0)), InsertBehavior::UnderNode(&current_node))
.unwrap();
};
}
CliOperation::Ls(entries) => {
*tree.get_mut(&current_node).unwrap().data_mut() = (
entries
.iter()
.filter_map(DirectoryEntry::as_file)
.map(|file| file.size)
.sum::<usize>(),
0,
)
}
}
}
let post_order_ids = tree
.traverse_post_order_ids(tree.root_node_id().unwrap())
.unwrap()
.collect::<Vec<_>>();
for id in post_order_ids {
let child_sum = tree
.children(&id)
.unwrap()
.map(|node| node.data().1)
.sum::<usize>();
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()
}

View File

@ -6,5 +6,6 @@ mod day03;
mod day04;
mod day05;
mod day06;
mod day07;
aoc_lib! { year = 2022 }