From 8ae0f0d1dac84303fc7f252c972fee9a3a0c6792 Mon Sep 17 00:00:00 2001 From: Justin Wernick Date: Wed, 7 Dec 2022 11:15:57 +0200 Subject: Day 7 --- 2022/src/bin/day_7.rs | 227 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 2022/src/bin/day_7.rs (limited to '2022/src/bin/day_7.rs') diff --git a/2022/src/bin/day_7.rs b/2022/src/bin/day_7.rs new file mode 100644 index 0000000..8933816 --- /dev/null +++ b/2022/src/bin/day_7.rs @@ -0,0 +1,227 @@ +use nom::{ + branch::alt, + bytes::complete::tag, + character::complete::{line_ending, not_line_ending, u32 as nom_u32}, + combinator::map, + multi::separated_list1, + sequence::{preceded, tuple}, + IResult, +}; +use std::fs; + +fn main() -> Result<(), Box> { + let input = fs::read_to_string("inputs/day_7.txt")?; + let terminal_log = TerminalLog::parser(&input).unwrap().1; + let dir_tree = terminal_log.process_to_dir_tree(); + + dbg!(dir_tree.sum_sizes_below_threshhold(100000)); + + let required_disk_usage = 70000000 - 30000000; + let required_deletion = dir_tree.size() - required_disk_usage; + dbg!(dir_tree.find_min_size_greater_than(required_deletion)); + + Ok(()) +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct TerminalLog(Vec); + +#[derive(Debug, PartialEq, Eq, Clone)] +enum TerminalLogItem { + Command(TerminalLogCommand), + Output(TerminalLogOutput), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +enum TerminalLogCommand { + CdRoot, + CdUp, + CdDown(String), + List, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +enum TerminalLogOutput { + Dir(String), + File(u32, String), +} + +impl TerminalLog { + fn parser(input: &str) -> IResult<&str, TerminalLog> { + map( + separated_list1(line_ending, TerminalLogItem::parser), + TerminalLog, + )(input) + } + + fn process_to_dir_tree(&self) -> DirTree { + use TerminalLogCommand::*; + use TerminalLogItem::*; + use TerminalLogOutput::*; + + let mut dir_tree_root = DirTree::root(); + let mut dir_path = Vec::new(); + + for log in &self.0 { + match log { + Command(CdRoot) => dir_path.clear(), + Command(CdUp) => { + dir_path.pop(); + } + Command(CdDown(dir_name)) => { + dir_path.push(dir_name.clone()); + } + Command(List) => {} + Output(Dir(dir_name)) => { + let dir = DirTree::new(dir_name.clone()); + dir_tree_root.push_dir(&dir_path, dir); + } + Output(File(size, name)) => { + let file = DirTreeFile { + name: name.clone(), + size: size.clone(), + }; + dir_tree_root.push_file(&dir_path, file); + } + } + } + + dir_tree_root + } +} + +impl TerminalLogItem { + fn parser(input: &str) -> IResult<&str, TerminalLogItem> { + alt(( + map( + preceded(tag("$ "), TerminalLogCommand::parser), + TerminalLogItem::Command, + ), + map(TerminalLogOutput::parser, TerminalLogItem::Output), + ))(input) + } +} + +impl TerminalLogCommand { + fn parser(input: &str) -> IResult<&str, TerminalLogCommand> { + alt(( + map(tag("cd /"), |_| TerminalLogCommand::CdRoot), + map(tag("cd .."), |_| TerminalLogCommand::CdUp), + map(preceded(tag("cd "), name_parser), |name| { + TerminalLogCommand::CdDown(name) + }), + map(tag("ls"), |_| TerminalLogCommand::List), + ))(input) + } +} + +impl TerminalLogOutput { + fn parser(input: &str) -> IResult<&str, TerminalLogOutput> { + alt(( + map(preceded(tag("dir "), name_parser), |name| { + TerminalLogOutput::Dir(name) + }), + map( + tuple((nom_u32, tag(" "), name_parser)), + |(size, _, name)| TerminalLogOutput::File(size, name), + ), + ))(input) + } +} + +fn name_parser(input: &str) -> IResult<&str, String> { + map(not_line_ending, |s: &str| s.to_owned())(input) +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct DirTree { + name: String, + files: Vec, + dirs: Vec, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct DirTreeFile { + name: String, + size: u32, +} + +impl DirTree { + fn root() -> DirTree { + DirTree { + name: "/".into(), + files: Vec::new(), + dirs: Vec::new(), + } + } + + fn new(name: String) -> DirTree { + DirTree { + name, + files: Vec::new(), + dirs: Vec::new(), + } + } + + fn push_dir(&mut self, path: &[String], dir: DirTree) { + if path.is_empty() { + self.dirs.push(dir); + } else { + let subdir = self + .dirs + .iter_mut() + .find(|d| d.name == path[0]) + .expect("Subdir doesn't exist"); + subdir.push_dir(&path[1..], dir); + } + } + + fn push_file(&mut self, path: &[String], dir: DirTreeFile) { + if path.is_empty() { + self.files.push(dir); + } else { + let subdir = self + .dirs + .iter_mut() + .find(|d| d.name == path[0]) + .expect("Subdir doesn't exist"); + subdir.push_file(&path[1..], dir); + } + } + + fn size(&self) -> u32 { + self.files.iter().map(|f| f.size).sum::() + + self.dirs.iter().map(|f| f.size()).sum::() + } + + fn sum_sizes_below_threshhold(&self, threshhold: u32) -> u32 { + let self_size = self.size(); + let self_size_below_threshold = if self_size < threshhold { self_size } else { 0 }; + self_size_below_threshold + + self + .dirs + .iter() + .map(|f| f.sum_sizes_below_threshhold(threshhold)) + .sum::() + } + + fn find_min_size_greater_than(&self, threshhold: u32) -> Option { + let min_subdir = self + .dirs + .iter() + .filter_map(|d| d.find_min_size_greater_than(threshhold)) + .min(); + + match min_subdir { + Some(min) => Some(min), + None => { + let self_size = self.size(); + if self_size > threshhold { + Some(self_size) + } else { + None + } + } + } + } +} -- cgit v1.2.3