diff --git a/Cargo.lock b/Cargo.lock index 2523ece..4f33286 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -21,6 +21,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" name = "advent_of_code" version = "0.11.0" dependencies = [ + "bisection", "chrono", "dhat", "itertools", @@ -74,6 +75,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "bisection" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e079a1bab0ecce6cf4b4b74c0c37afa4a697136eb3b127875c84a8f04a8c3" + [[package]] name = "bitflags" version = "1.3.2" diff --git a/Cargo.toml b/Cargo.toml index 9616f41..6b8378c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,4 @@ tinyjson = "2.5.1" # Solution dependencies regex = "1.11.1" itertools = "0.13.0" +bisection = "0.1.0" diff --git a/data/examples/07.txt b/data/examples/07.txt new file mode 100644 index 0000000..09a921e --- /dev/null +++ b/data/examples/07.txt @@ -0,0 +1,23 @@ +$ cd / +$ ls +dir a +14848514 b.txt +8504156 c.dat +dir d +$ cd a +$ ls +dir e +29116 f +2557 g +62596 h.lst +$ cd e +$ ls +584 i +$ cd .. +$ cd .. +$ cd d +$ ls +4060174 j +8033020 d.log +5626152 d.ext +7214296 k diff --git a/src/bin/07.rs b/src/bin/07.rs new file mode 100644 index 0000000..b50298d --- /dev/null +++ b/src/bin/07.rs @@ -0,0 +1,152 @@ +use bisection::bisect_left; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +advent_of_code::solution!(7); + +pub fn part_one(input: &str) -> Option { + let fs = parse_input(input); + + let mut ans = 0; + let dir_sizes = fs.get_dir_sizes(); + + for size in dir_sizes { + if size <= 100000 { + ans += size; + } + } + + Some(ans) +} + +pub fn part_two(input: &str) -> Option { + let fs = parse_input(input); + + let mut dir_sizes = fs.get_dir_sizes(); + dir_sizes.sort(); + + let needed_size = dir_sizes.last().unwrap() - 40000000; + let i = bisect_left(&dir_sizes, &needed_size); + + Some(dir_sizes[i]) +} + +struct Dir { + id: String, + parent: Option>>, + dirs: HashMap>>, + files: HashMap, +} + +impl Dir { + fn new(id: String, parent: Option>>) -> Self { + Self { + id, + parent, + dirs: HashMap::new(), + files: HashMap::new(), + } + } +} + +struct FileSystem { + root: Rc>, + cd: Rc>, +} + +impl FileSystem { + fn new() -> Self { + let root = Rc::new(RefCell::new(Dir::new("/".to_string(), None))); + let cd = root.clone(); + Self { root, cd } + } + + fn cd(&mut self, dir_id: String) { + if dir_id == "/" { + self.cd = self.root.clone(); + } else if dir_id == ".." { + let parent = self.cd.borrow().parent.clone(); + + if let Some(parent) = parent { + self.cd = parent; + } + } else { + let new_dir = self + .cd + .borrow_mut() + .dirs + .entry(dir_id.clone()) + .or_insert_with(|| { + Rc::new(RefCell::new(Dir::new( + dir_id.clone(), + Some(self.cd.clone()), + ))) + }) + .clone(); + + self.cd = new_dir; + } + } + + fn add_file(&self, file: (&str, &str)) { + self.cd + .borrow_mut() + .files + .insert(file.1.to_string(), file.0.parse::().unwrap()); + } + + fn get_dir_sizes(&self) -> Vec { + fn dfs(dir: Rc>, _dir_sizes: &mut Vec) -> u32 { + let dir = dir.borrow(); + + let mut size = dir.files.values().sum(); + for child in dir.dirs.values() { + size += dfs(child.clone(), _dir_sizes); + } + _dir_sizes.push(size); + + size + } + + let mut dir_sizes = vec![]; + + dfs(self.root.clone(), &mut dir_sizes); + + dir_sizes + } +} + +fn parse_input(input: &str) -> FileSystem { + let mut fs = FileSystem::new(); + + input.lines().for_each(|line| match line { + line if line.starts_with("$ cd") => { + let id = line.trim_start_matches("$ cd "); + fs.cd(id.to_string()); + } + line if line.starts_with("$ ls") => {} + line if line.starts_with("dir") => {} + _ => { + let file = line.split_once(" ").unwrap(); + fs.add_file(file); + } + }); + + fs +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let result = part_one(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(95437)); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + assert_eq!(result, Some(24933642)); + } +}