summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMartin Ashby <martin@ashbysoft.com>2022-12-07 21:30:24 +0000
committerMartin Ashby <martin@ashbysoft.com>2022-12-07 22:00:15 +0000
commit7383b348a746ac340e3058d733edc59a7c893e32 (patch)
treed80b93952ab8efedd12f61c8763ece4d0a120559 /src
parent745b6bfd5730a926a32c34d0b6e50a5685cbcddc (diff)
downloadaoc2022-7383b348a746ac340e3058d733edc59a7c893e32.tar.gz
aoc2022-7383b348a746ac340e3058d733edc59a7c893e32.tar.bz2
aoc2022-7383b348a746ac340e3058d733edc59a7c893e32.tar.xz
aoc2022-7383b348a746ac340e3058d733edc59a7c893e32.zip
day7
This one was a little trickier, as the obvious solution for navigating back up a tree is to keep a reference to the parent. However that's essentially a doubly linked list which is a pain in the ass in Rust. Instead, use a separate variable for the current 'key' into the tree I'm not especially happy with the terminal processing as well, since after the 'ls' command we need to 'peek' at the next entry to see if the 'ls' has finished and we're onto a next command I couldn't think of a very good way to do this except loops and loops.
Diffstat (limited to 'src')
-rw-r--r--src/day7.rs132
-rw-r--r--src/main.rs2
2 files changed, 134 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
diff --git a/src/main.rs b/src/main.rs
index 0be4d7f..b370693 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ mod day3;
mod day4;
mod day5;
mod day6;
+mod day7;
fn main() {
day1::run(fs::read_to_string("input/day1.txt").expect("Failed to read input file!"));
@@ -14,4 +15,5 @@ fn main() {
day4::run(fs::read_to_string("input/day4.txt").expect("Failed to read input file!"));
day5::run(fs::read_to_string("input/day5.txt").expect("Failed to read input file!"));
day6::run(fs::read_to_string("input/day6.txt").expect("Failed to read input file!"));
+ day7::run(fs::read_to_string("input/day7.txt").expect("Failed to read input file!"));
}