Improve day07

This commit is contained in:
Malte Tammena 2022-12-07 20:58:55 +01:00
parent 96869bff2f
commit 7ad37b1a89
3 changed files with 100 additions and 121 deletions

9
Cargo.lock generated
View file

@ -117,9 +117,6 @@ dependencies = [
[[package]]
name = "day07"
version = "0.1.0"
dependencies = [
"ego-tree",
]
[[package]]
name = "day08"
@ -189,12 +186,6 @@ version = "0.1.0"
name = "day24"
version = "0.1.0"
[[package]]
name = "ego-tree"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a68a4904193147e0a8dec3314640e6db742afd5f6e634f428a6af230d9b3591"
[[package]]
name = "errno"
version = "0.2.8"

View file

@ -8,4 +8,3 @@ build = true
app = true
[dependencies]
ego-tree = "0.6.2"

View file

@ -1,13 +1,13 @@
use ego_tree::NodeId;
#![feature(binary_heap_into_iter_sorted)]
use std::{
cmp::Reverse,
collections::BinaryHeap,
fs::File,
io::{BufRead, BufReader},
path::PathBuf,
};
type NodeMut<'t> = ego_tree::NodeMut<'t, Node>;
type NodeRef<'t> = ego_tree::NodeRef<'t, Node>;
type Tree = ego_tree::Tree<Node>;
#[derive(Debug, Default, PartialEq, Eq)]
enum SolvePuzzle {
First,
@ -16,147 +16,95 @@ enum SolvePuzzle {
}
#[derive(Debug)]
struct Node {
label: String,
size: Option<usize>,
dir: bool,
struct Collector<Ext: CollectorExt> {
pwd: PathBuf,
sums: Vec<usize>,
ext: Ext,
}
struct TreeParser {
tree: Tree,
curr_node: NodeId,
reading_dir_list: bool,
trait CollectorExt: Default {
fn change_directory_up(&mut self, dir_size: usize);
}
impl TreeParser {
pub fn new() -> Self {
let tree = Tree::new(Node {
label: String::from("/"),
size: None,
dir: true,
});
Self {
curr_node: tree.root().id(),
tree,
reading_dir_list: false,
}
}
#[derive(Debug, Default)]
struct PartOne {
small_dirs_sum: usize,
}
fn read(&mut self, line: &str) {
self.reading_dir_list &= !line.starts_with("$");
#[derive(Debug, Default)]
struct PartTwo {
smallest_relevant_dirs: BinaryHeap<Reverse<usize>>,
}
impl<Ext: CollectorExt> Collector<Ext> {
pub fn push_line<S: AsRef<str>>(&mut self, line: S) {
let line = line.as_ref();
if line == "$ cd /" {
self.curr_node = self.tree.root().id();
self.change_director_root()
} else if line == "$ cd .." {
let node = self.curr_node();
self.curr_node = node.parent().unwrap().id();
self.change_directory_up()
} else if line.starts_with("$ cd ") {
let node = self.curr_node();
let target = &line[5..];
let target = node
.children()
.find(|child| child.value().label == target)
.expect("Cd target exists");
self.curr_node = target.id();
self.change_directory_to(target)
} else if line == "$ ls" {
self.reading_dir_list = true;
} else if self.reading_dir_list == true && line.starts_with("dir ") {
let label = &line[4..];
let mut node = self.curr_node_mut();
node.append(Node {
label: label.to_owned(),
size: None,
dir: true,
});
} else if self.reading_dir_list {
let (size, label) = line.split_once(' ').expect("A dir list entry");
let mut node = self.curr_node_mut();
node.append(Node {
label: label.to_owned(),
size: Some(size.parse().expect("Filesize is number")),
dir: false,
});
// Just ignore
} else if line.starts_with("dir ") {
// Nothing to do
} else {
panic!("What? {:?}", line);
let (size, _label) = line.split_once(' ').expect("A dir list entry");
self.push_file(size.parse().expect("A number for dir size"))
}
}
fn curr_node(&self) -> NodeRef {
self.tree.get(self.curr_node).unwrap()
fn change_director_root(&mut self) {
while self.pwd.as_os_str() != "/" {
self.change_directory_up()
}
}
fn curr_node_mut(&mut self) -> NodeMut {
self.tree.get_mut(self.curr_node).unwrap()
fn change_directory_up(&mut self) {
let this_dir_size = self.sums.pop().expect("At least two elements here");
*self.sums.last_mut().expect("At least one element here") += this_dir_size;
self.pwd.pop();
self.ext.change_directory_up(this_dir_size)
}
fn finish(mut self) -> Tree {
let node_id = self.tree.root().id();
fill_size(&mut self.tree, node_id);
self.tree
fn change_directory_to(&mut self, target: &str) {
self.pwd.push(target);
self.sums.push(0);
}
}
fn fill_size(tree: &mut Tree, node_id: NodeId) {
let node_ref = tree.get(node_id);
let missing: Vec<_> = {
node_ref
.unwrap()
.children()
.filter(|child| child.value().size.is_none())
.map(|child| child.id())
.collect()
};
for miss in missing {
fill_size(tree, miss);
fn push_file(&mut self, size: usize) {
*self.sums.last_mut().expect("At least one element here") += size;
}
let node_ref = tree.get(node_id);
let size = node_ref
.unwrap()
.children()
.map(|child| child.value().size)
.map(Option::unwrap)
.sum();
tree.get_mut(node_id).unwrap().value().size = Some(size);
}
fn solve_first_puzzle<R: BufRead>(reader: R) -> usize {
let parser = reader
reader
.lines()
.map(Result::unwrap)
.fold(TreeParser::new(), |mut parser, line| {
parser.read(&line);
parser
});
let tree = parser.finish();
tree.nodes()
.map(|node_ref| node_ref.value())
.filter(|node| node.dir == true)
.map(|node| node.size.unwrap())
.filter(|&size| size <= 100000)
.sum()
.collect::<Collector<PartOne>>()
.ext
.small_dirs_sum
}
fn solve_second_puzzle<R: BufRead>(reader: R) -> usize {
const REQUIRED_SIZE: usize = 30_000_000;
const TOTAL: usize = 70_000_000;
let parser = reader
let col = reader
.lines()
.map(Result::unwrap)
.fold(TreeParser::new(), |mut parser, line| {
parser.read(&line);
parser
});
let tree = parser.finish();
let root_size = tree.root().value().size.unwrap();
.collect::<Collector<PartTwo>>();
let root_size = col.sums.first().unwrap();
let free = TOTAL - root_size;
let missing = REQUIRED_SIZE.saturating_sub(free);
tree.nodes()
.map(|node_ref| node_ref.value())
.filter(|node| node.dir == true)
.map(|node| node.size)
.map(Option::unwrap)
.filter(|&size| size >= missing)
.min()
.expect("At least one node")
col.ext
.smallest_relevant_dirs
.into_iter_sorted()
.find(|&Reverse(size)| size >= missing)
// There is at least the root dir
.unwrap()
.0
}
fn main() {
@ -178,6 +126,47 @@ fn main() {
println!("{}", solution);
}
impl<S, Ext> FromIterator<S> for Collector<Ext>
where
S: AsRef<str>,
Ext: CollectorExt,
{
fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
let mut col = iter.into_iter().fold(Self::default(), |mut acc, line| {
let line = line.as_ref();
acc.push_line(line);
acc
});
col.change_director_root();
col
}
}
impl<Ext: CollectorExt> Default for Collector<Ext> {
fn default() -> Self {
Self {
ext: Ext::default(),
pwd: PathBuf::from("/"),
sums: vec![0],
}
}
}
impl CollectorExt for PartOne {
fn change_directory_up(&mut self, dir_size: usize) {
const SMALL_DIR_MAX_SIZE: usize = 100000;
if dir_size < SMALL_DIR_MAX_SIZE {
self.small_dirs_sum += dir_size;
}
}
}
impl CollectorExt for PartTwo {
fn change_directory_up(&mut self, dir_size: usize) {
self.smallest_relevant_dirs.push(Reverse(dir_size))
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;