-
Notifications
You must be signed in to change notification settings - Fork 0
/
day_07.rs
147 lines (121 loc) · 3.71 KB
/
day_07.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::collections::HashMap;
use itertools::Itertools;
fn change_dir<'a>(path: &'a str, current_dir: Vec<&'a str>) -> Vec<&'a str> {
match path {
"/" => vec!["/"],
".." => {
let mut new_dir = current_dir.clone();
new_dir.pop();
new_dir
}
_ => {
let mut new_dir = current_dir.clone();
new_dir.push(path);
new_dir
}
}
}
fn is_command(line_segment: Option<&&str>) -> bool {
line_segment.unwrap().eq(&"$")
}
fn parse_input(input: &str) -> Vec<(Vec<&str>, u32)> {
let mut current_dir = vec!["/"];
let mut lines = input.lines().peekable();
let mut files: Vec<(Vec<&str>, u32)> = vec![];
while lines.peek().is_some() {
let line = lines.next().unwrap();
let mut segments = line.split_whitespace().peekable();
if !is_command(segments.peek()) {
continue;
}
// Consume the '$' character
segments.next();
match segments.next().unwrap() {
"cd" => current_dir = change_dir(segments.next().unwrap(), current_dir.clone()),
"ls" => {
while lines.peek().is_some() && !lines.peek().unwrap().starts_with('$') {
let line = lines.next().unwrap();
if line.starts_with("dir") {
continue;
}
let file_size = line
.split_whitespace()
.next()
.unwrap()
.parse::<u32>()
.unwrap();
files.push((current_dir.clone(), file_size));
}
}
_ => panic!("Unknown command"),
}
}
files
}
fn compute_dirs(files: Vec<(Vec<&str>, u32)>) -> HashMap<String, u32> {
let mut dirs: HashMap<String, u32> = HashMap::new();
for file in files.clone().iter() {
let mut dir_path = String::from("");
for seg in file.0.iter() {
dir_path += &format!("/{}", &seg);
dirs.insert(dir_path.clone(), dirs.get(&dir_path).unwrap_or(&0) + file.1);
}
}
dirs
}
pub fn part_1(input: &str) -> usize {
let files = parse_input(input);
let dirs = compute_dirs(files);
let max_size = 100_000;
let sum: u32 = dirs
.iter()
.filter(|(_, size)| **size <= max_size)
.map(|(_, size)| size)
.sum();
sum as usize
}
pub fn part_2(input: &str) -> usize {
let files = parse_input(input);
let dirs = compute_dirs(files);
let drive_size = 70_000_000;
let space_needed = 30_000_000;
let available_space = drive_size - dirs.get("//").unwrap_or(&0);
let target_dir_size = space_needed - available_space;
let dir_size = dirs
.iter()
.filter(|(_, size)| **size >= target_dir_size)
.map(|(_, size)| size)
.sorted()
.next()
.unwrap();
*dir_size as usize
}
#[cfg(test)]
mod tests {
use crate::get_input;
use super::*;
#[test]
fn test_part_1_dummy() {
let input = get_input("2022", "07", Some("dummy"));
let output = part_1(&input);
assert_eq!(output, 95437);
}
#[test]
fn test_part_1() {
let input = get_input("2022", "07", None);
let output = part_1(&input);
assert_eq!(output, 1391690);
}
#[test]
fn test_part_2_dummy() {
let input = get_input("2022", "07", Some("dummy"));
let output = part_2(&input);
assert_eq!(output, 24933642);
}
#[test]
fn test_part_2() {
let input = get_input("2022", "07", None);
let output = part_2(&input);
assert_eq!(output, 5469168);
}
}