2022-12-07 15:51:46 +01:00
#![ feature(test, if_let_guard) ]
2022-12-07 11:07:07 +01:00
extern crate test ;
use aoc2022 ::{ boilerplate , common ::* } ;
const DAY : usize = 7 ;
enum Node < ' a > {
2022-12-07 15:51:46 +01:00
File ( usize ) ,
2022-12-07 14:03:06 +01:00
Dir ( & ' a str , Vec < Node < ' a > > , usize ) ,
2022-12-07 11:07:07 +01:00
}
2022-12-07 11:42:32 +01:00
impl < ' a > Node < ' a > {
2022-12-07 15:51:46 +01:00
fn subdir_mut ( & mut self , dir : & str ) -> Option < & mut Self > {
2022-12-07 11:07:07 +01:00
match self {
2022-12-07 15:51:46 +01:00
Self ::Dir ( _ , contents , _ ) = > contents . iter_mut ( ) . find ( | d | matches! ( * * d , Self ::Dir ( name , _ , _ ) if name = = dir ) ) ,
_ = > None ,
2022-12-07 11:07:07 +01:00
}
}
}
fn parse_input ( raw : & str ) -> Node < '_ > {
let mut pwd = Vec ::< & str > ::new ( ) ;
2022-12-07 11:20:02 +01:00
let mut fs = Node ::Dir ( " / " , Vec ::new ( ) , 0 ) ;
2022-12-07 15:51:46 +01:00
for cmd in raw . trim_start_matches ( " $ cd / \n $ " ) . split ( " \n $ " ) {
match cmd . bytes ( ) . next ( ) {
2022-12-08 12:32:14 +01:00
Some ( b 'c' ) = > if cmd . ends_with ( '.' ) { pwd . pop ( ) ; } else { pwd . push ( & cmd [ 3 .. ] ) } ,
2022-12-07 11:07:07 +01:00
// ls
2022-12-07 15:51:46 +01:00
_ if let Some ( Node ::Dir ( _ , contents , _ ) ) = pwd . iter ( ) . try_fold ( & mut fs , | cd , p | cd . subdir_mut ( p ) ) = > contents . extend (
cmd . lines ( )
2022-12-07 11:42:32 +01:00
. filter_map ( | l | l . split_once ( ' ' ) )
. map ( | line | match line {
2022-12-07 14:03:06 +01:00
( " dir " , d ) = > Node ::Dir ( d , Vec ::new ( ) , 0 ) ,
2022-12-07 15:51:46 +01:00
( size , _ ) = > Node ::File ( parse_num ( size ) )
2022-12-07 11:42:32 +01:00
} )
2022-12-07 15:51:46 +01:00
) ,
2022-12-07 11:42:32 +01:00
_ = > unreachable! ( )
2022-12-07 11:07:07 +01:00
} ;
}
2022-12-07 11:42:32 +01:00
compute_dir_sizes ( & mut fs ) ;
2022-12-07 11:07:07 +01:00
fs
}
2022-12-07 11:42:32 +01:00
fn compute_dir_sizes ( node : & mut Node < '_ > ) -> usize {
2022-12-07 11:20:02 +01:00
match node {
2022-12-07 15:51:46 +01:00
Node ::File ( s ) = > * s ,
2022-12-07 11:20:02 +01:00
Node ::Dir ( _ , c , size ) = > {
2022-12-08 12:32:14 +01:00
* size = c . iter_mut ( ) . map ( compute_dir_sizes ) . sum ( ) ;
2022-12-07 11:20:02 +01:00
* size
}
}
}
2022-12-07 11:07:07 +01:00
fn part1 ( parsed : & Node < '_ > ) -> usize {
2022-12-07 11:20:02 +01:00
let mut sizes = Vec ::new ( ) ;
dir_sizes ( parsed , & mut sizes ) ;
sizes . into_iter ( ) . filter ( | & s | s < = 100_000 ) . sum ( )
2022-12-07 11:07:07 +01:00
}
2022-12-07 11:20:02 +01:00
fn dir_sizes ( node : & Node < '_ > , sizes : & mut Vec < usize > ) {
if let Node ::Dir ( _ , contents , size ) = node {
sizes . push ( * size ) ;
for c in contents {
dir_sizes ( c , sizes ) ;
2022-12-07 11:07:07 +01:00
}
}
}
fn part2 ( parsed : & Node < '_ > ) -> usize {
2022-12-07 11:20:02 +01:00
let needed_space = 30000000 - ( 70000000 - if let Node ::Dir ( _ , _ , s ) = parsed { s } else { unreachable! ( ) } ) ;
let mut sizes = Vec ::new ( ) ;
dir_sizes ( parsed , & mut sizes ) ;
sizes . into_iter ( ) . filter ( | & s | s > = needed_space ) . min ( ) . unwrap ( )
2022-12-07 11:07:07 +01:00
}
boilerplate! {
2022-12-09 12:50:12 +01:00
TEST_INPUT = = " \
$ cd /
2022-12-07 11:07:07 +01:00
$ 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 " ,
tests : {
part1 : { TEST_INPUT = > 95437 } ,
part2 : { TEST_INPUT = > 24933642 } ,
} ,
bench1 = = 1667443 ,
2022-12-07 11:42:32 +01:00
bench2 = = 8998590 ,
2022-12-07 15:51:46 +01:00
bench_parse : dir_name = > " / " ,
2022-12-07 11:07:07 +01:00
}
#[ cfg(test) ]
2022-12-07 15:51:46 +01:00
fn dir_name < ' a > ( n : & Node < ' a > ) -> & ' a str {
2022-12-07 11:07:07 +01:00
match n {
2022-12-07 11:20:02 +01:00
Node ::Dir ( name , _ , _ ) = > name ,
2022-12-07 15:51:46 +01:00
Node ::File ( _ ) = > panic! ( " Not a directory " ) ,
2022-12-07 11:07:07 +01:00
}
}