package main import ( "errors" "fmt" "log" "sort" "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { inp := h.StdinToStringSlice() part1(inp) fmt.Println("") part2(inp) } func buildFileSystem(inp []string) *dir { root := NewDir(nil, []string{}, "/") cwd := root for i := range inp { if i == 0 { // We've already done this continue } pts := strings.Fields(inp[i]) switch pts[0] { case "$": if pts[1] == "cd" { if pts[2] == ".." { cwd = cwd.Parent() } else { ch, err := cwd.ChangeDir(pts[2]) if err != nil { log.Fatalf("Failed to change directories: %s/%s, %s", strings.Join(cwd.Path(), "/"), pts[2], err.Error()) } cwd = ch } } case "dir": cwd.AddDir(pts[1]) default: cwd.AddFile(pts[1], h.Atoi(pts[0])) } } return root } func part1(inp []string) { fmt.Println("# Part 1") root := buildFileSystem(inp) smallDirs := root.FindDirectoriesMaxSize(100000) var sum int for i := range smallDirs { sum += smallDirs[i].Size() } fmt.Printf("Naive Size of Small Dirs: %d\n", sum) } func part2(inp []string) { fmt.Println("# Part 2") root := buildFileSystem(inp) total := 70000000 required := 30000000 used := root.Size() needed := required - (total - used) dir := root.FindSmallestDirectoryAtLeast(needed) if dir == nil { log.Fatal("No suitable directories found") } fmt.Printf("Disk Space %d / %d. Need %d more for update.\n", (total - used), total, needed) fmt.Println("Auto-deleting smallest directory that will permit update... Don't panic.") fmt.Printf("Freed space by deleting %s: %d\n", dir.FullPath(), dir.Size()) } const ( TpFile = iota TpDir ) type node interface { Parent() *dir Path() []string Name() string Type() int Size() int FullPath() []string Print(depth int) } type file struct { parent *dir path []string name string size int } func NewFile(parent *dir, path []string, nm string, size int) *file { return &file{ parent: parent, path: path, name: nm, size: size, } } func (f file) Parent() *dir { return f.parent } func (f file) Path() []string { return f.path } func (f file) Name() string { return f.name } func (f file) Type() int { return TpFile } func (f file) Size() int { return f.size } func (f file) FullPath() []string { return append(f.path, f.name) } func (f file) Print(depth int) { fmt.Printf("%s- %s (file, size=%d)\n", strings.Repeat(" ", depth), f.Name(), f.Size()) } type dir struct { parent *dir path []string name string contents map[string]node contentsList []string } func NewDir(parent *dir, path []string, name string) *dir { return &dir{ parent: parent, path: path, name: name, contents: make(map[string]node), } } func (d dir) Parent() *dir { return d.parent } func (d dir) Path() []string { return d.path } func (d dir) Name() string { return d.name } func (d dir) Type() int { return TpDir } func (d dir) Size() int { var size int for i := range d.contents { size += d.contents[i].Size() } return size } func (d dir) FullPath() []string { return append(d.path, d.name) } func (d *dir) ChangeDir(to string) (*dir, error) { if v, ok := d.contents[to]; !ok { return nil, errors.New("Directory not found") } else { switch d := v.(type) { case *dir: return d, nil default: return nil, errors.New("Not a directory") } } } func (d *dir) AddFile(nm string, size int) { d.contents[nm] = NewFile(d, d.FullPath(), nm, size) d.contentsList = append(d.contentsList, nm) sort.Strings(d.contentsList) } func (d *dir) AddDir(nm string) { d.contents[nm] = NewDir(d, d.FullPath(), nm) d.contentsList = append(d.contentsList, nm) sort.Strings(d.contentsList) } func (d dir) Print(depth int) { fmt.Printf("%s- %s (dir)\n", strings.Repeat(" ", depth), d.Name()) for _, nm := range d.contentsList { n, ok := d.contents[nm] if !ok { log.Fatalf("Error printing directory %s: %s not found.", d.FullPath(), nm) } n.Print(depth + 1) } } func (d *dir) FreeSpace(driveSize int) int { return driveSize - d.Size() } func (d *dir) FindDirectoriesMaxSize(size int) []*dir { var ret []*dir for _, node := range d.contents { switch child := node.(type) { case *dir: if child.Size() <= size { ret = append(ret, child) } ret = append(ret, child.FindDirectoriesMaxSize(size)...) } } return ret } func (d *dir) FindSmallestDirectoryAtLeast(needed int) *dir { var ret *dir retSize := d.Size() for _, node := range d.contents { switch child := node.(type) { case *dir: sz := child.Size() if sz >= needed && sz < retSize { ret = child retSize = sz rec := child.FindSmallestDirectoryAtLeast(needed) if rec != nil { ret = rec retSize = rec.Size() } } } } return ret }