day07 finished

This commit is contained in:
Ruediger Ludwig 2023-02-05 06:54:42 +01:00
parent 5a6abbd73c
commit 745abc936e
4 changed files with 1502 additions and 1 deletions

23
data/day07/example01.txt Normal file
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

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
View 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(())
}
}

View file

@ -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)),
} }
} }