11from textwrap import indent
2- from typing import Generator
2+ from itertools import chain
3+ from typing import Iterable , Generator , Self
34
4- # really just here for the type hinting
5- class FSO : # FSO: FileSystemObject
6- @property
7- def name (self ) -> str :
8- return self ._name
9-
10- @property
11- def size (self ) -> int :
12- return self ._size
135
14- class File ( FSO ) :
6+ class File :
157 def __init__ (self , size : str , name : str ) -> None :
16- self ._size = int (size )
17- self ._name = name
18-
8+ self .size = int (size )
9+ self .name = name
10+
1911 def __repr__ (self ) -> str :
2012 return f"- { self .name } (file, size={ self .size } )"
2113
22- class Directory (FSO ):
14+
15+ class Directory :
2316 def __init__ (self , name : str ) -> None :
24- self ._name = name
25- self .contents : dict [str , FSO ] = {}
26-
27- def add (self , child : FSO ) -> None :
28- self .contents [child .name ] = child
29-
30- # couldn't figure out how to get the type hinting correct on this one
31- # for some reason can't type hint Generator[Directory, None, None]
17+ self .name = name
18+ self .contents : dict [str , File | Directory ] = {}
19+
3220 @property
33- def dirs (self ) -> Generator [FSO , None , None ]:
21+ def dirs (self ) -> Generator [Self , None , None ]: # type: ignore
3422 yield self
35- for gen in [child .dirs for child in self .contents .values () if type (child )== Directory ]:
36- yield from gen
37-
38- @property
39- def dir_sizes (self ):
40- return (i .size for i in self .dirs )
41-
23+ yield from chain .from_iterable (
24+ child .dirs for child in self .contents .values () if type (child ) == Directory
25+ )
26+
4227 @property
43- def _size (self ) -> int :
28+ def size (self ) -> int :
4429 return sum (child .size for child in self .contents .values ())
45-
30+
4631 def __repr__ (self ) -> str :
47- children = '\n ' .join (indent (str (child ), ' ' * 2 )
48- for child in self .contents .values ())
32+ children = "\n " .join (
33+ indent (str (child ), " " * 2 ) for child in self .contents .values ()
34+ )
4935 return f"- { self .name } (dir)\n { children } "
5036
37+
5138class Filesystem :
5239 def __init__ (self ) -> None :
53- self .root : Directory = Directory ("/" )
54- self .head : list [Directory ] = [self .root ] # (c/sh)ould be a doubly-linked list
55-
56- @property
57- def tail (self ) -> Directory :
58- assert len (self .head )> 0 # sanity check
59- return self .head [- 1 ]
40+ self .root = Directory ("/" )
41+ self .head : list [Directory ] = [self .root ]
6042
6143 def cd (self , name : str ) -> None :
6244 match name :
63- case "/" : # jump to root
45+ case "/" : # jump to root
6446 self .head = [self .root ]
65- case ".." : # up one
66- assert len (self .head )> 1 # sanity check
47+ case ".." : # up one
6748 self .head .pop ()
68- case _: # everything else is a cd-into-child
69- assert name in self .tail .contents .keys () # sanity check
70- self .head .append (self .tail .contents [name ])
71-
49+ case _: # everything else is a cd-into-child
50+ child = self .head [- 1 ].contents [name ]
51+ assert type (child ) == Directory
52+ self .head .append (child )
53+
7254 def __repr__ (self ) -> str :
7355 return str (self .root )
7456
75- def parse ( lines : list [ str ]) -> Filesystem :
76- fs = Filesystem ()
57+
58+ def build ( lines : Iterable [ str ], fs : Filesystem ) -> Filesystem :
7759 for line in lines :
78- if line [0 ]== "$" : # the line is a command, either `ls` or `cd`
79- match line [2 :4 ]:
80- case "cd" :
81- fs .cd (line .split (" " )[- 1 ])
82- case "ls" :
83- pass # falls through to the else block on next line
84- case _: # sanity check
85- raise ValueError (f"command { line [2 :]} not in (`ls`, `cd`)" )
86- else : # the line is a result from an `ls` command
87- item = line .split (" " )
88- assert len (item )== 2 # sanity check
89- left , right = item
90- match left :
91- case "dir" :
92- fs .tail .add (Directory (right ))
93- case _:
94- fs .tail .add (File (left , right ))
60+ if line [0 :4 ] == "$ cd" :
61+ fs .cd (line [5 :])
62+ elif line [0 ] != "$" :
63+ child : File | Directory = (
64+ Directory (data [1 ])
65+ if (data := line .split (" " ))[0 ] == "dir"
66+ else File (* data )
67+ )
68+ fs .head [- 1 ].contents [child .name ] = child
9569 return fs
9670
71+
9772with open ("input7.txt" ) as f :
98- lines = [line .strip () for line in f ]
99- fs = parse (lines )
73+ fs = build ((line .strip () for line in f ), Filesystem ())
10074
101- print (sum (filter ( lambda x : x <= 1e5 , fs .root .dir_sizes ))) # part 1
102- print (min (filter ( lambda x : x > fs .root .size - int ( 4e7 ), fs . root . dir_sizes ))) # part 2
75+ print (sum (x . size for x in fs .root .dirs if x . size <= 1e5 )) # type: ignore
76+ print (min (x . size for x in fs . root . dirs if x . size > fs .root .size - 4e7 )) # type: ignore
0 commit comments