Solution for 2022/day07-part1
This commit is contained in:
parent
02afcc631b
commit
2946a6a208
@ -1,7 +1,7 @@
|
|||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, fmt, rc::Rc};
|
||||||
|
|
||||||
// Custom data structure
|
// Custom data structure
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||||
enum NodeType {
|
enum NodeType {
|
||||||
File,
|
File,
|
||||||
Directory,
|
Directory,
|
||||||
@ -9,11 +9,27 @@ enum NodeType {
|
|||||||
|
|
||||||
use NodeType::*;
|
use NodeType::*;
|
||||||
|
|
||||||
|
// #[derive(Debug)]
|
||||||
struct Node {
|
struct Node {
|
||||||
node_type: NodeType,
|
node_type: NodeType,
|
||||||
name: String,
|
name: String,
|
||||||
size: usize,
|
size: usize,
|
||||||
children: Vec<Rc<RefCell<Node>>>,
|
children: Vec<Rc<RefCell<Node>>>,
|
||||||
|
parent: Option<Rc<RefCell<Node>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
impl Node {
|
||||||
@ -23,6 +39,7 @@ impl Node {
|
|||||||
name: String::from(name),
|
name: String::from(name),
|
||||||
size,
|
size,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
|
parent: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,13 +57,20 @@ fn main() {
|
|||||||
let input = input.lines();
|
let input = input.lines();
|
||||||
|
|
||||||
// --- TASK BEGIN ---
|
// --- TASK BEGIN ---
|
||||||
let mut result = 0;
|
|
||||||
|
|
||||||
// Set up our filesystem, starting with the root directory as the root node.
|
// Set up our filesystem, starting with the root directory as the root node.
|
||||||
let root = Node::new(Directory, "/", 0);
|
let root = Node::new(Directory, "/", 0);
|
||||||
|
|
||||||
// Also set up a
|
// Also keep track of all directories in the graph separately.
|
||||||
let current = root;
|
// This will help us actually determine the sum we're supposed to look for.
|
||||||
|
let mut dir_list: Vec<Rc<RefCell<Node>>> = 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
|
// Read line by line
|
||||||
for line in input {
|
for line in input {
|
||||||
@ -55,11 +79,111 @@ fn main() {
|
|||||||
|
|
||||||
// First of all, differentiate between command and list item.
|
// First of all, differentiate between command and list item.
|
||||||
if line[0] == "$" {
|
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<RefCell<Node>> 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 {
|
} 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::<usize>().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<RefCell<Node>>) -> 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user