day07 finished
This commit is contained in:
parent
5a6abbd73c
commit
745abc936e
4 changed files with 1502 additions and 1 deletions
23
data/day07/example01.txt
Normal file
23
data/day07/example01.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
|
||||||
1135
data/day07/input.txt
Normal file
1135
data/day07/input.txt
Normal file
File diff suppressed because it is too large
Load diff
341
src/days/day07/mod.rs
Normal file
341
src/days/day07/mod.rs
Normal file
|
|
@ -0,0 +1,341 @@
|
||||||
|
use std::{
|
||||||
|
cell::{Cell, RefCell},
|
||||||
|
num::ParseIntError,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::template::{DayTrait, ResultType};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
const DAY_NUMBER: usize = 7;
|
||||||
|
|
||||||
|
pub struct Day;
|
||||||
|
|
||||||
|
impl DayTrait for Day {
|
||||||
|
fn get_day_number(&self) -> usize {
|
||||||
|
DAY_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
let root = Directory::parse(lines)?;
|
||||||
|
let result = root
|
||||||
|
.iter()
|
||||||
|
.filter_map(|dir| {
|
||||||
|
let size = dir.size();
|
||||||
|
if size <= 100000 {
|
||||||
|
Some(size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.sum();
|
||||||
|
Ok(ResultType::Integer(result))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(&self, lines: &[String]) -> anyhow::Result<ResultType> {
|
||||||
|
const AVAIL: i64 = 70_000_000;
|
||||||
|
const REQUIRED: i64 = 30_000_000;
|
||||||
|
let root = Directory::parse(lines)?;
|
||||||
|
let to_free = REQUIRED - (AVAIL - root.size());
|
||||||
|
let result = root
|
||||||
|
.iter()
|
||||||
|
.filter_map(|dir| {
|
||||||
|
let size = dir.size();
|
||||||
|
if size >= to_free {
|
||||||
|
Some(size)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.min()
|
||||||
|
.unwrap_or(0);
|
||||||
|
Ok(ResultType::Integer(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum DirectoryError {
|
||||||
|
#[error("Directory does not exist: {0}")]
|
||||||
|
NoSuchDirectory(String),
|
||||||
|
|
||||||
|
#[error("Directory has no parent")]
|
||||||
|
NoParentDirectory,
|
||||||
|
|
||||||
|
#[error("Illegal command: {0}")]
|
||||||
|
IllegalCommand(String),
|
||||||
|
|
||||||
|
#[error("No a legal Number")]
|
||||||
|
IllegalNumber(#[from] ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Node {
|
||||||
|
parent: Option<Weak<RefCell<Node>>>,
|
||||||
|
name: String,
|
||||||
|
sub_dirs: Vec<Rc<RefCell<Node>>>,
|
||||||
|
file_size: i64,
|
||||||
|
size: Cell<Option<i64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
fn root() -> Node {
|
||||||
|
Node {
|
||||||
|
parent: None,
|
||||||
|
name: "/".to_owned(),
|
||||||
|
sub_dirs: vec![],
|
||||||
|
file_size: 0,
|
||||||
|
size: Cell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_subdir(&mut self, subdir: Rc<RefCell<Node>>) {
|
||||||
|
self.sub_dirs.push(subdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_file(&mut self, _name: &str, size: i64) {
|
||||||
|
self.file_size += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> i64 {
|
||||||
|
self.size.get().unwrap_or_else(|| {
|
||||||
|
let subsize: i64 = self.sub_dirs.iter().map(|sub| sub.borrow().size()).sum();
|
||||||
|
let size = subsize + self.file_size;
|
||||||
|
|
||||||
|
self.size.set(Some(size));
|
||||||
|
size
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Directory {
|
||||||
|
node: Rc<RefCell<Node>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Directory {
|
||||||
|
pub fn root() -> Directory {
|
||||||
|
Directory {
|
||||||
|
node: Rc::new(RefCell::new(Node::root())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create(node: Rc<RefCell<Node>>) -> Directory {
|
||||||
|
Directory { node }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.node.borrow().name.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn size(&self) -> i64 {
|
||||||
|
self.node.borrow().size()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn file_size(&self) -> i64 {
|
||||||
|
self.node.borrow().file_size
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_subdir(&mut self, name: &str) {
|
||||||
|
let subdir = Rc::new(RefCell::new(Node {
|
||||||
|
parent: Some(Rc::downgrade(&self.node)),
|
||||||
|
name: name.to_owned(),
|
||||||
|
sub_dirs: vec![],
|
||||||
|
file_size: 0,
|
||||||
|
size: Cell::new(None),
|
||||||
|
}));
|
||||||
|
self.node.borrow_mut().add_subdir(subdir);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file(&mut self, name: &str, size: i64) {
|
||||||
|
self.node.borrow_mut().add_file(name, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parent(&self) -> Option<Directory> {
|
||||||
|
self.node
|
||||||
|
.borrow()
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|node| node.upgrade())
|
||||||
|
.map(|node| Directory { node })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_subdir(&self, name: &str) -> Option<Directory> {
|
||||||
|
let node = self.node.borrow();
|
||||||
|
let sub_node = node.sub_dirs.iter().find(|node| node.borrow().name == name);
|
||||||
|
sub_node.map(|node| Directory::create(node.clone()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(lines: &[String]) -> Result<Directory, DirectoryError> {
|
||||||
|
let root = Directory::root();
|
||||||
|
let mut current = root.clone();
|
||||||
|
for line in lines {
|
||||||
|
match line.split_whitespace().collect::<Vec<&str>>()[..] {
|
||||||
|
["$", "cd", dir] => {
|
||||||
|
current = match dir {
|
||||||
|
"/" => root.clone(),
|
||||||
|
".." => {
|
||||||
|
let Some(next) = current.parent() else {
|
||||||
|
return Err(DirectoryError::NoParentDirectory);
|
||||||
|
};
|
||||||
|
next
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let Some(next) = current.get_subdir(dir) else {
|
||||||
|
return Err(DirectoryError::NoSuchDirectory(dir.to_owned()))
|
||||||
|
};
|
||||||
|
next
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
["$", "ls"] => {} // Command can safely be ignored
|
||||||
|
["dir", name] => {
|
||||||
|
current.add_subdir(name);
|
||||||
|
}
|
||||||
|
[size, name] => {
|
||||||
|
let size = size.parse::<i64>()?;
|
||||||
|
current.add_file(name, size);
|
||||||
|
}
|
||||||
|
_ => return Err(DirectoryError::IllegalCommand(line.to_owned())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(root)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter(&self) -> DirectoryIterator {
|
||||||
|
DirectoryIterator::create(self.node.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DirectoryIterator {
|
||||||
|
directory: Rc<RefCell<Node>>,
|
||||||
|
subdirectory: Option<(usize, Box<DirectoryIterator>)>,
|
||||||
|
finished: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirectoryIterator {
|
||||||
|
pub fn create(directory: Rc<RefCell<Node>>) -> DirectoryIterator {
|
||||||
|
DirectoryIterator {
|
||||||
|
directory,
|
||||||
|
subdirectory: None,
|
||||||
|
finished: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for DirectoryIterator {
|
||||||
|
type Item = Directory;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.finished {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if let Some((subdirectory, sub_iterator)) = self.subdirectory.as_mut() {
|
||||||
|
if let Some(next) = sub_iterator.next() {
|
||||||
|
Some(next)
|
||||||
|
} else {
|
||||||
|
let dir = self.directory.borrow();
|
||||||
|
let subdirectory = *subdirectory + 1;
|
||||||
|
if subdirectory < dir.sub_dirs.len() {
|
||||||
|
let subdir = dir.sub_dirs[subdirectory].clone();
|
||||||
|
let mut sub_iterator = DirectoryIterator::create(subdir);
|
||||||
|
let result = sub_iterator.next();
|
||||||
|
self.subdirectory = Some((subdirectory, Box::new(sub_iterator)));
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
self.finished = true;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let dir = self.directory.borrow();
|
||||||
|
if !dir.sub_dirs.is_empty() {
|
||||||
|
let subdir = dir.sub_dirs[0].clone();
|
||||||
|
let sub_iterator = DirectoryIterator::create(subdir);
|
||||||
|
self.subdirectory = Some((0, Box::new(sub_iterator)));
|
||||||
|
} else {
|
||||||
|
self.finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Directory {
|
||||||
|
node: self.directory.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::common::file::read_lines;
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_directory() {
|
||||||
|
let directory = Directory::root();
|
||||||
|
assert_eq!(directory.name(), "/");
|
||||||
|
assert_eq!(directory.size(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_create_and_get_subdir() {
|
||||||
|
let mut root = Directory::root();
|
||||||
|
root.add_file("file1", 100);
|
||||||
|
root.add_file("file2", 200);
|
||||||
|
root.add_subdir("subdir");
|
||||||
|
let mut subdir = root.get_subdir("subdir").unwrap();
|
||||||
|
subdir.add_file("file3", 300);
|
||||||
|
subdir.add_file("file4", 400);
|
||||||
|
|
||||||
|
assert_eq!(subdir.name(), "subdir");
|
||||||
|
assert_eq!(subdir.size(), 700);
|
||||||
|
assert_eq!(root.size(), 1000);
|
||||||
|
assert_eq!(root.file_size(), 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_total_size() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let dir = Directory::parse(&lines)?;
|
||||||
|
assert_eq!(dir.size(), 48381165);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_iterator() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = vec!["/", "a", "e", "d"];
|
||||||
|
let dir = Directory::parse(&lines)?;
|
||||||
|
let result: Vec<_> = dir.iter().map(|dir| dir.name()).collect();
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part1() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(95437);
|
||||||
|
let result = day.part1(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_part2() -> Result<()> {
|
||||||
|
let day = Day {};
|
||||||
|
let lines = read_lines(day.get_day_number(), "example01.txt")?;
|
||||||
|
let expected = ResultType::Integer(24933642);
|
||||||
|
let result = day.part2(&lines)?;
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ mod day03;
|
||||||
mod day04;
|
mod day04;
|
||||||
mod day05;
|
mod day05;
|
||||||
mod day06;
|
mod day06;
|
||||||
|
mod day07;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
pub use template::DayTrait;
|
pub use template::DayTrait;
|
||||||
|
|
@ -13,7 +14,7 @@ pub mod day_provider {
|
||||||
use super::*;
|
use super::*;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const MAX_DAY: usize = 6;
|
const MAX_DAY: usize = 7;
|
||||||
|
|
||||||
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
pub fn get_day(day_num: usize) -> Result<Box<dyn DayTrait>, ProviderError> {
|
||||||
match day_num {
|
match day_num {
|
||||||
|
|
@ -23,6 +24,7 @@ pub mod day_provider {
|
||||||
4 => Ok(Box::new(day04::Day)),
|
4 => Ok(Box::new(day04::Day)),
|
||||||
5 => Ok(Box::new(day05::Day)),
|
5 => Ok(Box::new(day05::Day)),
|
||||||
6 => Ok(Box::new(day06::Day)),
|
6 => Ok(Box::new(day06::Day)),
|
||||||
|
7 => Ok(Box::new(day07::Day)),
|
||||||
_ => Err(ProviderError::InvalidNumber(day_num)),
|
_ => Err(ProviderError::InvalidNumber(day_num)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue