213 lines
4.8 KiB
Go
213 lines
4.8 KiB
Go
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
|
|
}
|