diff --git a/d7/Cargo.lock b/d7/Cargo.lock new file mode 100644 index 0000000..30cae7a --- /dev/null +++ b/d7/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "d7" +version = "0.1.0" diff --git a/d7/Cargo.toml b/d7/Cargo.toml new file mode 100644 index 0000000..22b9faf --- /dev/null +++ b/d7/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "d7" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/d7/src/main.rs b/d7/src/main.rs new file mode 100644 index 0000000..53696bc --- /dev/null +++ b/d7/src/main.rs @@ -0,0 +1,127 @@ +use std::{cell::RefCell, collections::HashMap, fs::read_to_string, io::Result, rc::Rc}; + +#[derive(Debug)] +enum Entry { + Dir(Option, HashMap), + File(usize), +} + +type RME = Rc>; + +impl Entry { + pub fn new_dir>(parent: &Option, name: S) -> RME { + let this = Rc::new(RefCell::new(Entry::Dir(parent.clone(), HashMap::new()))); + let this_clone = this.clone(); + match parent { + Some(parent) => match &mut *parent.borrow_mut() { + Entry::Dir(_, ref mut children) => { + children.insert(name.as_ref().to_owned(), this_clone); + } + _ => panic!("Attaching a child to a file"), + }, + None => (), + } + + this + } + + pub fn new_file>(parent: RME, name: S, size: usize) -> RME { + let this = Rc::new(RefCell::new(Entry::File(size))); + let this_clone = this.clone(); + match &mut *parent.borrow_mut() { + Entry::Dir(_, ref mut children) => { + children.insert(name.as_ref().to_owned(), this_clone); + } + _ => panic!("Attaching a child to a file"), + } + this + } + + pub fn size(&self) -> usize { + match self { + Entry::File(size) => *size, + Entry::Dir(_, ref children) => children.values().map(|f| f.borrow().size()).sum(), + } + } + + pub fn parent(&self) -> RME { + match self { + Entry::Dir(parent, _) => parent.as_ref().unwrap().clone(), + _ => panic!("Get parent for file"), + } + } + + pub fn file>(&self, name: S) -> RME { + match self { + Entry::Dir(_, ref children) => children.get(name.as_ref()).unwrap().clone(), + _ => panic!("Get file for file"), + } + } + + pub fn iter_dirs(&self, f: &mut F) { + match self { + Entry::Dir(_, ref children) => { + f(self); + for child in children.values() { + child.borrow().iter_dirs(f); + } + } + _ => (), + } + } +} + +fn main() -> Result<()> { + let none = None; + let root = Entry::new_dir(&none, "/"); + + let mut current = root.clone(); + + for line in read_to_string("input.txt")? + .split("\n") + .filter(|x| !x.is_empty()) + { + if line.starts_with("$ cd ") { + let path = line.split_at(5).1; + + if path == "/" { + current = root.clone(); + } else if path == ".." { + let next = current.borrow().parent(); + current = next; + } else { + let next = current.borrow().file(path); + current = next; + } + } else if line == "$ ls" { + // ignore + } else if line.starts_with("dir ") { + let path = line.split_at(4).1; + Entry::new_dir(&Some(current.clone()), path); + } else { + let split = line.split(" ").collect::>(); + let size = split[0].parse::().unwrap(); + let path = split[1]; + Entry::new_file(current.clone(), path, size); + } + } + + let mut sum_p1 = 0; + let mut smallest_p2 = usize::MAX; + + let to_free = 30000000usize - (70000000usize - root.borrow().size()); + root.borrow().iter_dirs(&mut |dir| { + let size = dir.size(); + if size <= 100000 { + sum_p1 += size + } + if size >= to_free && size <= smallest_p2 { + smallest_p2 = size + } + }); + + println!("Part 1: {}", sum_p1); + println!("Part 2: {}", smallest_p2); + + Ok(()) +}