use std::{ cell::RefCell, rc::{Rc, Weak}, }; #[derive(Debug)] enum NodeType { File(usize), Dir(RefCell>>), } #[derive(Debug)] struct Node { name: String, typ: NodeType, parent: Weak, } impl Node { fn get_size(&self) -> usize { let mut size = 0; match &self.typ { NodeType::File(filesize) => size = *filesize, NodeType::Dir(dir_nodes) => { for node in dir_nodes.borrow().iter() { size += node.get_size() } } } size } } fn parse_input(input: &str) -> Rc { let root_node: Rc = Rc::new(Node { name: String::from("/"), typ: NodeType::Dir(RefCell::new(Vec::new())), parent: Weak::new(), }); let mut current_dir: Rc = root_node.clone(); for line in input.lines() { if line.is_empty() { continue; } let mut split = line.split_whitespace(); let first = split.next().unwrap(); let second = split.next().unwrap(); match (first, second) { ("$", "cd") => { let next = split.next().unwrap(); if next != "/" { match next { ".." => { let bla = current_dir.parent.upgrade().unwrap(); current_dir = bla; } dir => { let mut new_dir = current_dir.clone(); if let NodeType::Dir(nodes) = ¤t_dir.typ { new_dir = nodes .borrow() .iter() .find(|n| n.name == dir) .unwrap() .clone(); } current_dir = new_dir; } } } } ("$", "ls") => (), (typ_size, name) => { if let Ok(size) = typ_size.parse::() { let new_node = Node { name: name.to_string(), typ: NodeType::File(size), parent: Rc::downgrade(¤t_dir), }; if let NodeType::Dir(nodes) = ¤t_dir.typ { nodes.borrow_mut().push(Rc::new(new_node)); } } else { let new_node = Node { name: name.to_string(), typ: NodeType::Dir(RefCell::new(Vec::new())), parent: Rc::downgrade(¤t_dir), }; if let NodeType::Dir(nodes) = ¤t_dir.typ { nodes.borrow_mut().push(Rc::new(new_node)); } } } } } root_node } fn find(node: Rc, max_size: usize) -> usize { let mut total_size = 0; if let NodeType::Dir(nodes) = &node.typ { for node in nodes.borrow().iter() { if let NodeType::Dir(_) = &node.typ { let size = node.get_size(); if size < max_size { total_size += size; } total_size += find(node.clone(), max_size); } } } total_size } fn find_best_fit(node: Rc, size: usize) -> usize { let mut actual_size = usize::MAX; if let NodeType::Dir(nodes) = &node.typ { for node in nodes.borrow().iter() { if let NodeType::Dir(_) = &node.typ { let node_size = node.get_size(); if node_size >= size { if actual_size > node_size { actual_size = node_size; } let deeper = find_best_fit(node.clone(), size); if actual_size > deeper { actual_size = deeper; } } } } } actual_size } pub fn task_one(input: &str) -> String { let root = parse_input(&input); // if let NodeType::Dir(nodes) = &root.typ { // println!("{:?}", nodes.borrow()) // } find(root, 100_000).to_string() } pub fn task_two(input: &str) -> String { const TOTAL_SPACE: usize = 70_000_000; const REQUIRED_FREE_SPACE: usize = 30_000_000; let root = parse_input(&input); let required_space = REQUIRED_FREE_SPACE - (TOTAL_SPACE - root.get_size()); find_best_fit(root, required_space).to_string() }