summaryrefslogtreecommitdiff
path: root/src/day7.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/day7.rs')
-rw-r--r--src/day7.rs132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/day7.rs b/src/day7.rs
new file mode 100644
index 0000000..e0df644
--- /dev/null
+++ b/src/day7.rs
@@ -0,0 +1,132 @@
+use std::{collections::HashMap};
+
+// Making a recursive data structure in Rust is a pain in the ass.
+// So don't bother, just link one way (nodes own their children)
+// And we'll separately track our 'current working directory' with a
+// vector of keys into the tree
+#[derive(Debug)]
+struct Node {
+ children: HashMap<String, Node>,
+ // Intrinsic size of the file at this location.
+ size: Option<u32>,
+ // Is the location a directory. Technically should be able to tell by presence/absence of children, but
+ // empty directories are a thing.
+ isdir: bool,
+}
+
+impl Node {
+ fn new(isdir: bool) -> Node {
+ Node {
+ children: HashMap::new(),
+ size: None,
+ isdir: isdir,
+ }
+ }
+ fn set(&mut self, path: &Vec<&str>, size: Option<u32>, isdir: bool) {
+ let mut cn = self;
+ for ch in path {
+ cn = cn.children.entry((*ch).to_owned()).or_insert(Node::new(true));
+ }
+ cn.size = size;
+ cn.isdir = isdir;
+ }
+ /**
+ * Returns a map of total, recursive directory sizes,by relative path from the current directory
+ * Empty string key is the current directory size
+ */
+ fn dir_sizes(self) -> HashMap<String, u32> {
+ let mut res: HashMap<String, u32> = HashMap::new();
+ let mut mysize: u32 = 0;
+ for (name, nn) in self.children {
+ if let Some(fz) = nn.size {
+ // It's a file, just add the size
+ mysize += fz;
+ } else {
+ // It's a directory, add the size _and_ all the entries with subdir prefixed
+ let child_ds = nn.dir_sizes();
+ let child_sz = child_ds[""];
+ mysize += child_sz;
+ for (cn, sz) in child_ds {
+ res.insert(format!("{}/{}", name, cn), sz);
+ }
+ }
+ }
+ res.insert("".to_owned(), mysize);
+ res
+ }
+}
+
+pub fn run(input: String) {
+ let mut root = Node::new(true);
+ let mut cwd: Vec<&str>= vec![];
+ let lines: Vec<&str> = input.lines().collect();
+ let mut i = 0;
+ loop {
+ if i >= lines.len() {
+ break;
+ }
+ let line = lines[i];
+ let toks: Vec<&str> = line.split_whitespace().collect();
+ if toks[0] != "$" {
+ panic!("expected a command, got {}",line);
+ }
+ match toks[1] {
+ "cd" => {
+ i += 1;
+ let dirname = toks[2];
+ if dirname == "/" {
+ cwd.clear();
+ } else if dirname == ".." {
+ cwd.pop();
+ } else {
+ cwd.push(dirname);
+ }
+ root.set(&cwd, None, true);
+ }
+ "ls" => {
+ i += 1;
+ loop {
+ if i >= lines.len() {
+ break;
+ }
+ let lsline = lines[i];
+ let lstoks: Vec<&str> = lsline.split_whitespace().collect();
+ if lstoks[0] == "$" {
+ // This is the next command, loop back to the top without incrementing!
+ break;
+ }
+ let (fsize, isdir) = if lstoks[0] == "dir" {
+ (None, true)
+ } else {
+ (Some(lstoks[0].parse().expect("filesize wasn't a u32!")), false)
+ };
+ let fname = lstoks[1];
+ cwd.push(fname);
+ root.set(&cwd, fsize, isdir);
+ cwd.pop();
+ i += 1;
+ }
+ }
+ cmd => {
+ panic!("unknown command {}", cmd)
+ }
+ }
+ }
+ let ds = root.dir_sizes();
+ // let mut tot: u32 = 0;
+ // for (_, sz) in ds {
+ // if sz <= 100000 {
+ // tot += sz;
+ // }
+ // }
+ // println!("Day 7: {}", tot);
+ let fs_size:u32 = 70000000;
+ let total_used = ds.get("").unwrap();
+ let total_free = fs_size - total_used;
+ let update_size:u32 = 30000000;
+ let need_to_free = update_size - total_free;
+ let ddsz = ds.values()
+ .filter(|sz| { **sz >= need_to_free })
+ .fold(fs_size, |acc, e| { acc.min(*e) });
+ println!("Day 7: {}", ddsz);
+} \ No newline at end of file