···11+import fileinput
22+33+44+class File:
55+ def __init__(self, name, size=0):
66+ self.name = name
77+ self.size = size
88+ self.children = []
99+ self.parent = None
1010+1111+FS = File('/')
1212+pwd = FS
1313+1414+for line in fileinput.input():
1515+ if line.startswith('$'):
1616+ parts = line.split()
1717+ cmd = parts[1]
1818+ if cmd == 'cd':
1919+ directory = parts[2]
2020+ if directory == '..':
2121+ pwd = pwd.parent
2222+ else: # cd-ing into a new directory
2323+ file = File(directory)
2424+ file.parent = pwd
2525+ pwd.children.append(file)
2626+ pwd = file
2727+2828+ # The current line is telling us something about
2929+ # either a file and its size, or a new directory.
3030+ else:
3131+ size, name = line.split()
3232+3333+ # Only care if we are looking at a file.
3434+ if size != 'dir':
3535+ file = File(name, int(size))
3636+ pwd.children.append(file)
3737+3838+3939+SIZES = {}
4040+4141+def dir_size(file):
4242+ """Return the size of the given file."""
4343+ if not file.children:
4444+ return file.size
4545+4646+ total_size = 0
4747+ for child in file.children:
4848+ total_size += dir_size(child)
4949+5050+ SIZES[file] = total_size
5151+ return total_size
5252+5353+# Perform DFS to seed `SIZES`.
5454+USED = dir_size(FS)
5555+5656+# Part 1
5757+print("Part 1:", sum(s for s in SIZES.values() if s < 100000))
5858+5959+# Part 2
6060+AVAILABLE = 70000000
6161+NEED = 30000000
6262+UNUSED = AVAILABLE - USED
6363+6464+for size in sorted(SIZES.values()):
6565+ if UNUSED + size >= NEED:
6666+ print("Part 2:", size)
6767+ break
6868+