From 2946a6a2087c4296acfdd3948ebe7b374032f7f6 Mon Sep 17 00:00:00 2001 From: Tobias Marschner Date: Sun, 11 Feb 2024 16:50:05 +0100 Subject: [PATCH] Solution for 2022/day07-part1 --- 2022/day07-part1/src/main.rs | 140 +++++++++++++++++++++++++++++++++-- 1 file changed, 132 insertions(+), 8 deletions(-) diff --git a/2022/day07-part1/src/main.rs b/2022/day07-part1/src/main.rs index 0df5291..18c5090 100644 --- a/2022/day07-part1/src/main.rs +++ b/2022/day07-part1/src/main.rs @@ -1,7 +1,7 @@ -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, fmt, rc::Rc}; // Custom data structure -#[derive(Copy, Clone, Debug)] +#[derive(Eq, PartialEq, Copy, Clone, Debug)] enum NodeType { File, Directory, @@ -9,11 +9,27 @@ enum NodeType { use NodeType::*; +// #[derive(Debug)] struct Node { node_type: NodeType, name: String, size: usize, children: Vec>>, + parent: Option>>, +} + +// Manual implementation of Debug for Node. +// Necessary in order to remove the "parent" field from the output. +// Otherwise the printer will print indefinitely. +impl fmt::Debug for Node { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Node") + .field("node_type", &self.node_type) + .field("name", &self.name) + .field("size", &self.size) + .field("children", &self.children) + .finish() + } } impl Node { @@ -23,6 +39,7 @@ impl Node { name: String::from(name), size, children: Vec::new(), + parent: None, })) } } @@ -40,13 +57,20 @@ fn main() { let input = input.lines(); // --- TASK BEGIN --- - let mut result = 0; // Set up our filesystem, starting with the root directory as the root node. let root = Node::new(Directory, "/", 0); - // Also set up a - let current = root; + // Also keep track of all directories in the graph separately. + // This will help us actually determine the sum we're supposed to look for. + let mut dir_list: Vec>> = Vec::new(); + dir_list.push(root.clone()); + + // Also set up a pointer for the current directory. + let mut cd = root.clone(); + + // Keep track whether we're currently reading directories. + let mut ls_mode = false; // Read line by line for line in input { @@ -55,11 +79,111 @@ fn main() { // First of all, differentiate between command and list item. if line[0] == "$" { - + match line[1] { + "cd" => { + // Changing directory. + ls_mode = false; + match line[2] { + "/" => { + // If the target is "/" switch back to the root directory. + cd = root.clone(); + } + ".." => { + // Switch to parent directory. + // First, we need to borrow the actual Node of the current directory. + let cd_node = cd.borrow(); + // Grab an Rc to the parent... + let pd = cd_node.parent.clone().unwrap(); + drop(cd_node); + // ...and assign it to cd. + cd = pd; + } + dir => { + // In the standard case, look for the child-node with the correct name. + // Get from the Rc> to the actual Node. + let cd_node = cd.borrow(); + // From there, find the child directory with the matching name. + let target_dir = cd_node + .children + .iter() + .find(|x| x.borrow().name == dir) + .unwrap() + .clone(); + // Drop the Ref<_, Node> to ensure we drop the borrow on cd. + drop(cd_node); + // Now assign the new "current directory". + cd = target_dir; + } + } + } + "ls" => { + ls_mode = true; + } + _ => (), + } } else { - println!("item"); + // Looks like we're in list mode... right? + assert!(ls_mode); + + // Assume it is a directory. + let mut node_type = Directory; + let mut size: usize = 0; + let name = line[1]; + + // If it is a file, update the size and type accordingly. + if line[0] != "dir" { + node_type = File; + size = line[0].parse::().unwrap(); + } + + // Now create the node for this file / directory. + let node = Node::new(node_type, name, size); + // Update the children of the current directory. + cd.borrow_mut().children.push(node.clone()); + // And ensure the new node points back to the current directory. + node.borrow_mut().parent = Some(cd.clone()); + + // Also add any new directories to the directory list. + if node_type == Directory { + dir_list.push(node.clone()); + } } } - println!("Result: {}", result); + // All files and directories have been parsed into the data structure. + // However, the size on all directories is currently 0. + // Determine the size of all directories recursively using DFS. + calc_node_size(root.clone()); + + // Finally, actually perform what the task requested. + let mut total: usize = 0; + for dir in &dir_list { + let size = dir.borrow().size; + if size <= 100000 { + total += size; + } + } + + println!("Result: {}", total); +} + +// Perform a depth-first-search on the tree in order to annotate the directory sizes. +// This method uses recursion to visit the entire tree. +fn calc_node_size(node: Rc>) -> usize { + let mut inner_node = node.borrow_mut(); + // If this node is a file, there's no recursion to be done. + // Simply return the size of the file. + if inner_node.node_type == File { + inner_node.size + } else { + let mut size: usize = 0; + // Iterate through all children and ... + for child in &inner_node.children { + // ... accumulate the size of all nodes recursively. + size += calc_node_size(child.clone()); + } + // One more thing: Update the size of this directory before returning. + inner_node.size = size; + size + } }