From 933cdbfd19cc0d2b6f9465de2ceb72183ac6c4d9 Mon Sep 17 00:00:00 2001 From: missing Date: Wed, 7 Dec 2022 14:13:48 -0600 Subject: [PATCH] day 7 --- Cargo.toml | 1 + src/days/day7.rs | 184 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 186 insertions(+) create mode 100644 src/days/day7.rs diff --git a/Cargo.toml b/Cargo.toml index 61ec6a2..3fed85a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" aoc-runner = "0.3.0" aoc-runner-derive = "0.3.0" itertools = "0.10.5" +slotmap = "1.0.6" diff --git a/src/days/day7.rs b/src/days/day7.rs new file mode 100644 index 0000000..01a6470 --- /dev/null +++ b/src/days/day7.rs @@ -0,0 +1,184 @@ +use std::collections::HashMap; + +use aoc_runner_derive::{aoc, aoc_generator}; +use slotmap::{new_key_type, SlotMap}; + +new_key_type! { + struct FsKey; +} + +struct Fs { + root_dir: FsKey, + entries: SlotMap, +} + +impl Fs { + fn new() -> Self { + let mut map = SlotMap::with_key(); + + let root_key = map.insert(FsEntry { + parent: None, + size: 0, + kind: FsEntryKind::Dir(HashMap::new()), + }); + + Self { + entries: map, + root_dir: root_key, + } + } + + fn visit_children(&self, root: FsKey, mut f: impl FnMut(&FsEntry)) { + self.__visit_children(root, &mut f); + } + + fn __visit_children(&self, root: FsKey, f: &mut impl FnMut(&FsEntry)) { + let entry = self.get(root); + + f(entry); + + if let FsEntryKind::Dir(children) = &entry.kind { + for &child in children.values() { + self.__visit_children(child, &mut *f); + } + } + } + + fn visit_parents_mut(&mut self, root: FsKey, mut f: impl FnMut(&mut FsEntry)) { + let entry = self.get_mut(root); + + f(entry); + + if let Some(parent_key) = entry.parent { + self.visit_parents_mut(parent_key, f); + } + } + + fn add(&mut self, name: String, entry: FsEntry) -> FsKey { + let entry_size = entry.size; + + let current_dir = entry.parent.unwrap(); + let entry_key = self.entries.insert(entry); + let current = self.get_mut(current_dir); + + let FsEntryKind::Dir(children) = &mut current.kind else { + panic!("not a directory"); + }; + + assert!(!children.contains_key(&name)); + children.insert(name, entry_key); + + self.visit_parents_mut(current_dir, |v| { + v.size += entry_size; + }); + + entry_key + } + + fn get(&self, key: FsKey) -> &FsEntry { + self.entries.get(key).unwrap() + } + + fn get_mut(&mut self, key: FsKey) -> &mut FsEntry { + self.entries.get_mut(key).unwrap() + } +} + +struct FsEntry { + parent: Option, + size: u64, + kind: FsEntryKind, +} + +enum FsEntryKind { + File, + Dir(HashMap), +} + +#[aoc_generator(day7)] +fn generator(input: &str) -> Fs { + let mut fs = Fs::new(); + + let mut current_dir = fs.root_dir; + + let mut lines = input.lines().peekable(); + while let Some(l) = lines.next() { + let l = l.strip_prefix("$ ").unwrap(); + + if let Some(name) = l.strip_prefix("cd") { + let name = name.trim(); + + if name == "/" { + current_dir = fs.root_dir; + } else if name == ".." { + current_dir = fs.get(current_dir).parent.unwrap(); + } else { + let FsEntryKind::Dir(children) = &fs.get(current_dir).kind else { + panic!("not a directory"); + }; + + current_dir = *children.get(name).unwrap(); + } + } else if l.trim() == "ls" { + while let Some(l) = lines.next_if(|v| !v.starts_with('$')) { + let (size_or_dir, name) = l.split_once(' ').unwrap(); + let name = name.trim().to_owned(); + + let entry = if size_or_dir == "dir" { + FsEntry { + parent: Some(current_dir), + size: 0, + kind: FsEntryKind::Dir(HashMap::new()), + } + } else { + FsEntry { + parent: Some(current_dir), + size: size_or_dir.parse().unwrap(), + kind: FsEntryKind::File, + } + }; + + fs.add(name, entry); + } + } + } + + fs +} + +#[aoc(day7, part1)] +fn part1_indexing(fs: &Fs) -> u64 { + let mut res = 0; + + fs.visit_children(fs.root_dir, |f| { + if let FsEntryKind::Dir(_) = f.kind { + if f.size <= 100000 { + res += f.size; + } + } + }); + + res +} + +#[aoc(day7, part2)] +fn part2(fs: &Fs) -> u64 { + const TOTAL_SPACE: u64 = 70_000_000; + const NEEDED_FREE_SPACE: u64 = 30_000_000; + + let used_space = fs.get(fs.root_dir).size; + let free_space = TOTAL_SPACE - used_space; + let need_to_free = NEEDED_FREE_SPACE - free_space; + + let mut res = u64::MAX; + + fs.visit_children(fs.root_dir, |f| { + if let FsEntryKind::Dir(_) = f.kind { + if f.size >= need_to_free && f.size < res { + res = f.size; + } + } + }); + + res +} diff --git a/src/lib.rs b/src/lib.rs index bbdb0ca..6a87c37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ mod days { mod day4; mod day5; mod day6; + mod day7; } aoc_lib! { year = 2022 }