diff --git a/day07/PathNode.py b/day07/PathNode.py new file mode 100644 index 0000000..3b171a0 --- /dev/null +++ b/day07/PathNode.py @@ -0,0 +1,75 @@ +class PathNode: + def __init__(self, name, size = 0): + self.name = name + self.size = size + self.parent = None + self.children = {} + + def has_parent(self): + # print('%s has a parent? %s' % (self.name, self.parent is not None)) + return self.parent is not None + + def set_parent(self, parent): + self.parent = parent + + def has_child(self, name): + # print('%s has child %s? %s' % (self.name, name, self.children.has_key(name))) + return self.children.has_key(name) + + def add_child(self, name, size = 0): + # print('+ adding %s to %s' % (name, self.name)) + self.children[name] = PathNode(name, size) + self.children[name].set_parent(self) + self.size += size + + ptr = self + while ptr.has_parent(): + ptr = ptr.parent + ptr.size += size + + def is_directory(self): + return len(self.children) > 0 + + def print_self(self, indentation = 0): + size = '(%d)' % (self.size) + (' [%d items]' % (len(self.children)) if self.is_directory() else '') + print('%s%s %s' % (' ' * indentation, self.name, size)) + for key in self.children: + self.children[key].print_self(indentation+1) + + def go_up_one_level(self): + if self.has_parent(): + # print('-> moved up one level from %s to %s' % (self.name, self.parent.name)) + return self.parent + return self + + def go_to_root(self): + if self.has_parent(): + # print('-> going to root from %s' % (self.name)) + return self.parent.go_to_root() + return self + + def go_to_child(self, name): + if self.has_child(name): + # print('-> going to %s from %s' % (name, self.name)) + return self.children[name] + return self + + def get_directories_le(self, size): + dirs = [] + for key in filter(lambda c: self.children[c].is_directory(), self.children): + if self.children[key].size <= size: + dirs.append([self.children[key].name, self.children[key].size]) + dirs += self.children[key].get_directories_le(size) + return dirs + + def iterate_all_directories(self): + dirs = [[self.name, self.size]] + for key in self.children: + dirs += self.children[key].iterate_all_directories() + return dirs + + def find_smallest_at_least(self, required_size): + all_dirs = self.iterate_all_directories() + adequate_size_dirst = filter(lambda item: item[1] >= required_size, all_dirs) + smallest_dir = min(adequate_size_dirst, key=lambda item: item[1]) + return smallest_dir diff --git a/day07/solve.py b/day07/solve.py new file mode 100644 index 0000000..f607447 --- /dev/null +++ b/day07/solve.py @@ -0,0 +1,68 @@ +# Day 07: No Space Left On Device + +import os +from PathNode import PathNode + +def get_path(): + return os.path.join(os.path.dirname(__file__), 'data.txt') + +def get_dir_tree(): + ptr = PathNode('/') + + with open(get_path()) as fp: + for cmd in fp: + cmd = cmd.strip() + # print(cmd) + if cmd[0] == '$': + # commands + if cmd[2:4] == 'ls': + pass # basically a noop + elif cmd[2:4] == 'cd': + if cmd[5:] == '/': + ptr = ptr.go_to_root() + elif cmd[5:] == '..': + ptr = ptr.go_up_one_level() + else: + ptr = ptr.go_to_child(cmd[5:]) + pass + elif cmd[:3] == 'dir': + # list a directory + ptr.add_child(cmd[4:]) + else: + # listing a file + [size, name] = cmd.split() + ptr.add_child(name, int(size)) + + return ptr.go_to_root() + + +def solve_part1(): + root = get_dir_tree() + # ptr.print_self() + dirs = root.get_directories_le(100000) + total_size = sum(map(lambda item: item[1], dirs)) + + print('Day 07: Part 1') + print('GOAL: Find all of the directories with a total size of at most 100000.') + print('ANSWER: What is the sum of the total sizes of those directories?') + print('Total size: %d' % (total_size)) + +def solve_part2(): + total_size = 70000000 + required_space = 30000000 + + root = get_dir_tree() + delete_size = required_space - (total_size - root.size) + # print('Current usage is %d / %d [additional %d needed to get to %d / %d]' % \ + # (root.size, total_size, delete_size, total_size - required_space, total_size)) + smallest = root.find_smallest_at_least(delete_size) + + print('Day 07: Part 2') + print('GOAL: Find the smallest directory that, if deleted, would free up enough space on the filesystem to run the update.') + print('ANSWER: What is the total size of that directory?') + print('Need to free up %d' % delete_size) + print('Smallest directory: %s - %d' % (smallest[0], smallest[1])) + +if __name__ == '__main__': + solve_part1() + solve_part2() \ No newline at end of file diff --git a/day07/tests.py b/day07/tests.py new file mode 100644 index 0000000..548086f --- /dev/null +++ b/day07/tests.py @@ -0,0 +1,12 @@ +import unittest + +class Day07TestMethods(unittest.TestCase): + def setUp(self): + pass + + # Does a thing + def test_true(self): + self.assertEqual(True, False) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/solve.py.template b/solve.py.template index 0c56228..e14ca6d 100644 --- a/solve.py.template +++ b/solve.py.template @@ -12,12 +12,15 @@ def solve_part1(): for line in fp: print(line) + print('Day #DAY#: Part 1') print('GOAL: #GOAL1#') print('ANSWER: #ANSWER1#') print('Total: %d' % (total)) + def solve_part2(): total = 0 - + + print('Day #DAY#: Part 2') print('GOAL: #GOAL2#') print('ANSWER: #ANSWER2#') print('Total: %d' % (total))