106 lines
2.9 KiB
Nim
106 lines
2.9 KiB
Nim
import std/[strutils, strformat, sequtils]
|
|
|
|
const input = readFile("day_07/input.txt")
|
|
|
|
type
|
|
FileKind = enum
|
|
fkFile,
|
|
fkDirectory
|
|
File = ref object
|
|
name: string
|
|
parent: File
|
|
case kind: FileKind
|
|
of fkFile:
|
|
size: uint64
|
|
of fkDirectory:
|
|
children: seq[File]
|
|
|
|
proc `$`(file: FIle): string =
|
|
result.add fmt"File(name: {file.name}, kind: {$file.kind}, "
|
|
if file.kind == fkDirectory:
|
|
let childrenAsStr = file.children.mapIt($it).join(",")
|
|
result.add fmt"children: [{childrenAsStr}]"
|
|
elif file.kind == fkFile:
|
|
result.add fmt"size: {file.size}"
|
|
result.add ")"
|
|
|
|
proc root(file: File): File =
|
|
if file.parent.isNil:
|
|
return file
|
|
return file.parent.root
|
|
|
|
proc calcSize(file: File): uint64 =
|
|
assert file.kind == fkDirectory
|
|
return file.children.mapIt(if it.kind == fkFIle: it.size else: it.calcSize).foldl(a + b, 0.uint64)
|
|
|
|
proc processCommand(wd: File, command: string, output: string): File =
|
|
case command.split(" ")[0]:
|
|
of "cd":
|
|
let path = command.split(" ")[1]
|
|
case path:
|
|
of "/":
|
|
return wd.root
|
|
of "..":
|
|
return wd.parent
|
|
else:
|
|
for child in wd.children:
|
|
if child.name == path:
|
|
return child
|
|
let newFile = File(name: path, parent: wd, kind: fkDirectory)
|
|
wd.children.add newFile
|
|
return newFile
|
|
of "ls":
|
|
for file in output.splitLines:
|
|
if file.isEmptyOrWhitespace: break
|
|
let desc = file.split(" ")[0]
|
|
let name = file.split(" ")[1]
|
|
if desc == "dir":
|
|
wd.children.add(File(name: name, parent: wd, kind: fkDirectory))
|
|
else:
|
|
wd.children.add(File(name: name, parent: wd, kind: fkFile, size: desc.parseBiggestUInt))
|
|
return wd
|
|
|
|
proc traverseDirectoryLog(data: string): File =
|
|
var wd = File(name: "/", parent: nil, kind: fkDirectory, children: newSeq[File]())
|
|
var command: string
|
|
var output: string
|
|
for line in data.splitLines:
|
|
if line.startsWith "$":
|
|
if not command.isEmptyOrWhitespace:
|
|
wd = processCommand(wd, command, output)
|
|
command = line[2..line.len-1]
|
|
output = ""
|
|
continue
|
|
output.add line & "\n"
|
|
return processCommand(wd, command, output).root
|
|
|
|
iterator traverse(dir: File): File {.closure.} =
|
|
assert dir.kind == fkDirectory
|
|
for child in dir.children:
|
|
if child.kind != fkDirectory: continue
|
|
yield child
|
|
for subchild in child.traverse:
|
|
yield subchild
|
|
|
|
proc solvePart1() =
|
|
let root = traverseDirectoryLog(input)
|
|
var sum: uint64 = 0
|
|
for dir in traverse(root):
|
|
if (dir.calcSize <= 100000):
|
|
sum += dir.calcSize
|
|
echo "Sum of dirs < 100000: " & $sum
|
|
|
|
proc solvePart2() =
|
|
let root = traverseDirectoryLog(input)
|
|
var minimumSize = 30000000 - (70000000 - root.calcSize)
|
|
var smallestDir = root.calcSize
|
|
for dir in traverse(root):
|
|
let size = dir.calcSize
|
|
if size < minimumSize: continue
|
|
smallestDir = min(smallestDir, size)
|
|
echo "Smalles dir for update: " & $smallestDir
|
|
|
|
if isMainModule:
|
|
solvePart1()
|
|
solvePart2()
|