Rust Day 7

This commit is contained in:
Tobias Berger 2022-12-07 09:55:05 +01:00
parent 24b28937f9
commit a296f62b58
Signed by: toby
GPG key ID: 2D05EFAB764D6A88
7 changed files with 1342 additions and 2 deletions

2
rust/Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 3
[[package]]
name = "advent-of-code"
version = "22.6.2"
version = "22.7.2"

View file

@ -1,6 +1,6 @@
[package]
name = "advent-of-code"
version = "22.6.2"
version = "22.7.2"
edition = "2021"
resolver = "2"
@ -47,6 +47,10 @@ path = "src/day05/main.rs"
name = "day06"
path = "src/day06/main.rs"
[[bin]]
name = "day07"
path = "src/day07/main.rs"
[dependencies]
# Required for one of the day06 solutions (commented out)
# phf = { version = "0.11.1", features = ["macros"] }

1101
rust/src/day07/input.txt Normal file

File diff suppressed because it is too large Load diff

81
rust/src/day07/main.rs Normal file
View file

@ -0,0 +1,81 @@
#![feature(map_try_insert)]
const INPUT: &str = include_str!("input.txt");
mod part_1;
use part_1::part_1;
mod part_2;
use part_2::part_2;
#[derive(Debug)]
pub(crate) enum ConsoleLine {
MoveRoot,
MoveUp,
MoveDown(&'static str),
List,
ListOutputDir(&'static str),
ListOutputFile(&'static str, usize),
}
#[derive(Debug)]
pub(crate) struct ConsoleLineParseError;
impl ConsoleLine {
fn from_str(s: &'static str) -> Result<Self, ConsoleLineParseError> {
let result = match s.starts_with('$') {
true => match &s[2..4] {
"cd" => match s.chars().nth(5) {
Some('/') => Ok(ConsoleLine::MoveRoot),
Some('.') => Ok(ConsoleLine::MoveUp),
Some(_) => Ok(ConsoleLine::MoveDown(&s[5..])),
None => Err(ConsoleLineParseError),
},
"ls" => Ok(ConsoleLine::List),
_ => Err(ConsoleLineParseError),
},
false => match s.chars().next() {
Some(char) => match char.is_numeric() {
true => {
let split_index = s.char_indices().find(|char| char.1 == ' ');
match split_index {
Some(split_index) => {
let (file_size, file_name) = s.split_at(split_index.0);
// Remove leading ' '
let file_name = &file_name[1..];
let file_size = file_size.parse::<usize>();
match file_size {
Ok(file_size) => {
Ok(ConsoleLine::ListOutputFile(file_name, file_size))
}
Err(_) => Err(ConsoleLineParseError),
}
}
None => Err(ConsoleLineParseError),
}
}
false => Ok(ConsoleLine::ListOutputDir(&s[4..])),
},
None => Err(ConsoleLineParseError),
},
};
if let Ok(result) = result {
// println!("parsed: {result:?}");
Ok(result)
} else {
result
}
}
}
fn parse_input(input: &'static str) -> Vec<ConsoleLine> {
input
.lines()
.map(|line| ConsoleLine::from_str(line).expect("Invalid input"))
.collect()
}
pub fn main() {
let input = parse_input(INPUT);
part_1(&input);
part_2(&input);
}

59
rust/src/day07/part_1.rs Normal file
View file

@ -0,0 +1,59 @@
use std::collections::HashMap;
use crate::ConsoleLine;
pub(crate) fn part_1(input: &Vec<ConsoleLine>) -> usize {
let mut current_directory: Vec<&'static str> = vec![];
let mut directory_sizes = HashMap::<String, usize>::new();
for line in input {
match line {
ConsoleLine::MoveRoot => {
while current_directory.len() > 1 {
current_directory.pop();
}
}
ConsoleLine::MoveUp => {
current_directory.pop();
}
ConsoleLine::MoveDown(new_dir) => {
current_directory.push(new_dir);
}
ConsoleLine::List => {}
ConsoleLine::ListOutputDir(_dir_name) => {}
ConsoleLine::ListOutputFile(_file_name, file_size) => {
for length in 0..=current_directory.len() {
let parent_directory = current_directory[0..length].join("/");
if let Err(mut a) = directory_sizes.try_insert(parent_directory, *file_size) {
let t = a.entry.get_mut();
*t += a.value;
};
}
}
}
}
let result = directory_sizes
.iter()
.filter_map(|(_, v)| if *v <= 100_000 { Some(v) } else { None })
.sum();
println!("Part 1: {result}",);
result
}
#[cfg(test)]
mod tests {
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
#[test]
fn test_with_solution() {
assert_eq!(super::part_1(&crate::parse_input(crate::INPUT)), 1084134);
}
#[test]
fn test_with_sample_solution() {
assert_eq!(super::part_1(&crate::parse_input(SAMPLE_INPUT)), 95437);
}
}

72
rust/src/day07/part_2.rs Normal file
View file

@ -0,0 +1,72 @@
use std::collections::HashMap;
use crate::ConsoleLine;
pub(crate) fn part_2(input: &Vec<ConsoleLine>) -> usize {
const TOTAL_SPACE: usize = 70_000_000;
const NEEDED_SPACE: usize = 30_000_000;
let mut current_directory: Vec<&'static str> = vec![];
let mut directory_sizes = HashMap::<String, usize>::new();
for line in input {
match line {
ConsoleLine::MoveRoot => {
while current_directory.len() > 1 {
current_directory.pop();
}
}
ConsoleLine::MoveUp => {
current_directory.pop();
}
ConsoleLine::MoveDown(new_dir) => {
current_directory.push(new_dir);
}
ConsoleLine::List => {}
ConsoleLine::ListOutputDir(_dir_name) => {}
ConsoleLine::ListOutputFile(_file_name, file_size) => {
for length in 0..=current_directory.len() {
let parent_directory = current_directory[0..length].join("/");
if let Err(mut a) = directory_sizes.try_insert(parent_directory, *file_size) {
let t = a.entry.get_mut();
*t += a.value;
};
}
}
}
}
let available_space = TOTAL_SPACE - directory_sizes[""];
let required_space = NEEDED_SPACE - available_space;
let result = directory_sizes
.iter()
.filter_map(|(_directory_name, directory_size)| {
if *directory_size >= required_space {
Some(*directory_size)
} else {
None
}
})
.min()
.unwrap();
println!("Part 2: {result}",);
result
}
#[cfg(test)]
mod tests {
const SAMPLE_INPUT: &str = include_str!("sample_input.txt");
#[test]
fn test_with_solution() {
assert_eq!(super::part_2(&crate::parse_input(crate::INPUT)), 6183184);
}
#[test]
fn test_with_sample_solution() {
assert_eq!(super::part_2(&crate::parse_input(SAMPLE_INPUT)), 24933642);
}
}

View file

@ -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