Day 7
This commit is contained in:
parent
d35e9844e0
commit
6f9ee71a9b
@ -8,3 +8,4 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
aoc-runner = "0.3.0"
|
aoc-runner = "0.3.0"
|
||||||
aoc-runner-derive = "0.3.0"
|
aoc-runner-derive = "0.3.0"
|
||||||
|
id_tree = "1.8.0"
|
||||||
|
182
src/day07.rs
Normal file
182
src/day07.rs
Normal 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(¤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::<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()
|
||||||
|
}
|
@ -6,5 +6,6 @@ mod day03;
|
|||||||
mod day04;
|
mod day04;
|
||||||
mod day05;
|
mod day05;
|
||||||
mod day06;
|
mod day06;
|
||||||
|
mod day07;
|
||||||
|
|
||||||
aoc_lib! { year = 2022 }
|
aoc_lib! { year = 2022 }
|
||||||
|
Loading…
Reference in New Issue
Block a user