Skip to content

Commit

Permalink
Completed day 07
Browse files Browse the repository at this point in the history
With graphs. Very cool
  • Loading branch information
Jazzinghen committed Dec 7, 2022
1 parent 4c3c82d commit ab41c97
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/year_2022.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod day03;
mod day04;
mod day05;
mod day06;
mod day07;

use crate::aoc_lib::DayFn;

Expand All @@ -15,7 +16,7 @@ pub fn get_day(day: u8) -> (Option<DayFn>, Option<DayFn>) {
4 => (Some(day04::part1), Some(day04::part2)),
5 => (Some(day05::part1), Some(day05::part2)),
6 => (Some(day06::part1), Some(day06::part2)),
7 => (None, None),
7 => (Some(day07::part1), Some(day07::part2)),
8 => (None, None),
9 => (None, None),
10 => (None, None),
Expand Down
212 changes: 212 additions & 0 deletions src/year_2022/day07.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::{char, digit1};
use nom::combinator::{map, rest};
use nom::sequence::{preceded, separated_pair};
use nom::IResult;
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::DfsPostOrder;
use std::cmp::Reverse;
use std::collections::BinaryHeap;

enum CLILogLine {
Entry(FSType),
Command(CLICommand),
}

enum FSType {
File((usize, String)),
Directory(String),
}

enum CLICommand {
Cd(String),
List,
}

// File parses
fn file_entry(input: &str) -> IResult<&str, FSType> {
map(
separated_pair(digit1, char(' '), rest),
|(file_size, file_name): (&str, &str)| {
FSType::File((file_size.parse().unwrap(), file_name.to_string()))
},
)(input)
}

fn folder_entry(input: &str) -> IResult<&str, FSType> {
map(preceded(tag("dir "), rest), |folder: &str| {
FSType::Directory(folder.to_string())
})(input)
}

fn cd_command(input: &str) -> IResult<&str, CLICommand> {
map(preceded(tag("cd "), rest), |folder: &str| {
CLICommand::Cd(folder.to_string())
})(input)
}

fn ls_command(input: &str) -> IResult<&str, CLICommand> {
let (rem_input, _) = tag("ls")(input)?;

Ok((rem_input, CLICommand::List))
}

fn cli_line(input: &str) -> IResult<&str, CLILogLine> {
if let Ok((rem_input, command)) = preceded(tag("$ "), alt((ls_command, cd_command)))(input) {
return Ok((rem_input, CLILogLine::Command(command)));
}

map(alt((file_entry, folder_entry)), |entry: FSType| {
CLILogLine::Entry(entry)
})(input)
}

fn build_fs_tree(input: &str) -> DiGraph<(String, usize), ()> {
let mut fs_tree: DiGraph<(String, usize), ()> = DiGraph::new();

let mut tree_stack: Vec<(String, NodeIndex)> = Vec::new();
let mut roots: Vec<NodeIndex> = Vec::new();

for line in input.lines().map(|l| l.trim()) {
if let Ok((_, log_line)) = cli_line(line) {
match log_line {
CLILogLine::Command(CLICommand::Cd(path)) => {
if path == ".." {
let _ = tree_stack.pop();
} else {
let new_dir = fs_tree.add_node((path.clone(), 0));
if let Some((_, parent_folder)) = tree_stack.last() {
fs_tree.add_edge(*parent_folder, new_dir, ());
} else {
roots.push(new_dir);
}
tree_stack.push((path, new_dir));
}
}
CLILogLine::Entry(FSType::File((size, name))) => {
let (_, curr_dir) = tree_stack.last().unwrap();
let new_file = fs_tree.add_node((name, size));
fs_tree.add_edge(*curr_dir, new_file, ());
}
_ => {}
}
}
}

for root in roots.into_iter() {
let mut dfs_post = DfsPostOrder::new(&fs_tree, root);
while let Some(node) = dfs_post.next(&fs_tree) {
if fs_tree.neighbors(node).count() > 0 {
let mut node_size: usize = 0;
for child in fs_tree.neighbors(node) {
node_size += fs_tree[child].1;
}
fs_tree[node].1 = node_size;
}
}
}

fs_tree
}

fn small_folders_sum(fs_tree: &DiGraph<(String, usize), ()>) -> usize {
let mut sum: usize = 0;
for node in fs_tree.node_indices() {
if fs_tree.neighbors(node).count() > 0 {
let folder_size = fs_tree[node].1;
if folder_size <= 100000 {
sum += folder_size;
}
}
}

sum
}

fn find_folder_to_delete(
fs_tree: &DiGraph<(String, usize), ()>,
update_size: usize,
total_space: usize,
) -> Result<usize, &str> {
let mut folders_heap: BinaryHeap<Reverse<usize>> = BinaryHeap::new();
let mut used_space: usize = 0;

for node in fs_tree.node_indices() {
if fs_tree.neighbors(node).count() > 0 {
folders_heap.push(Reverse(fs_tree[node].1));
used_space = used_space.max(fs_tree[node].1);
}
}

while let Some(next_size) = folders_heap.pop() {
let new_free_space = used_space - next_size.0;
if (total_space - new_free_space) >= update_size {
return Ok(next_size.0);
}
}

Err("Couldn't find a folder big enough to free enough space. Format the system.")
}

pub fn part1(input: &str) {
let tree = build_fs_tree(input);
let smol_sum = small_folders_sum(&tree);

println!("Sum of all the folders with size <= 100k: {}", smol_sum);
}

pub fn part2(input: &str) {
let tree = build_fs_tree(input);
let smallest_folder_to_delete = find_folder_to_delete(&tree, 30000000, 70000000).unwrap();

println!(
"Size of the smallest folder to delete if we want to install update: {}",
smallest_folder_to_delete
);
}

#[cfg(test)]
mod tests {
use super::*;

static INPUT_STRING: &str = "$ cd /
$ ls
dir a
14848514 b.txt
8504156 c.dat
dir d
$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
$ cd ..
$ cd d
$ ls
4060174 j
8033020 d.log
5626152 d.ext
7214296 k";

#[test]
fn simple_folder_size() {
let tree = build_fs_tree(INPUT_STRING);
let smol_sum = small_folders_sum(&tree);

assert_eq!(smol_sum, 95437usize);
}

#[test]
fn update_removal_size() {
let tree = build_fs_tree(INPUT_STRING);
let smallest_folder_to_delete = find_folder_to_delete(&tree, 30000000, 70000000).unwrap();

assert_eq!(smallest_folder_to_delete, 24933642usize);
}
}

0 comments on commit ab41c97

Please sign in to comment.