Rust Day 7
This commit is contained in:
parent
24b28937f9
commit
a296f62b58
7 changed files with 1342 additions and 2 deletions
2
rust/Cargo.lock
generated
2
rust/Cargo.lock
generated
|
@ -4,4 +4,4 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "advent-of-code"
|
||||
version = "22.6.2"
|
||||
version = "22.7.2"
|
||||
|
|
|
@ -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
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
81
rust/src/day07/main.rs
Normal 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
59
rust/src/day07/part_1.rs
Normal 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
72
rust/src/day07/part_2.rs
Normal 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);
|
||||
}
|
||||
}
|
23
rust/src/day07/sample_input.txt
Normal file
23
rust/src/day07/sample_input.txt
Normal 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
|
Loading…
Reference in a new issue