const sample = await Deno.readTextFile("sample.txt"); const input = await Deno.readTextFile("input.txt"); /**** TYPES *********************************/ interface LSCommandInfo { type: "command"; command: "ls"; } interface CDCommandInfo { type: "command"; command: "cd"; name: string; } interface FileInfo { type: "file"; size: number; name: string; } interface DirectoryInfo { type: "dir"; name: string; } type InfoItem = LSCommandInfo | CDCommandInfo | FileInfo | DirectoryInfo; class Directory { public parent: Directory | undefined; public name: string; public size = 0; public files: string[] = []; public dirs: Directory[] = []; constructor({ name, parent }: { name: string; parent?: Directory }) { this.name = name; this.parent = parent; } getDir(name: string): Directory | undefined { return this.dirs.find((dir) => dir.name === name); } hasDir(name: string): boolean { return this.dirs.some((dir) => dir.name === name); } hasFile(name: string): boolean { return this.files.some((file) => file === name); } addDir(name: string): Directory { if (this.hasDir(name)) { return this.getDir(name)!; } const newDir = new Directory({ parent: this, name }); this.dirs.push(newDir); return newDir; } get totalSize(): number { return ( this.size + this.dirs.reduce((total, dir) => total + dir.totalSize, 0) ); } } /**** FUNCTIONS *****************************/ const parseData = (data: string): InfoItem[] => { return data .split("\n") .filter(Boolean) .map((line) => { const parts = line.split(" "); if (parts[0] === "$") { return { type: "command", command: parts[1] === "ls" ? "ls" : "cd", name: parts.slice(2).join(" "), }; } else if (!isNaN(parseFloat(parts[0]))) { return { type: "file", size: parseFloat(parts[0]), name: parts[1] }; } else { return { type: "dir", name: parts.slice(1).join(" ") }; } }); }; const flatten = (dir: Directory): Directory[] => [ dir, ...dir.dirs.flatMap((dir) => flatten(dir)), ]; const printTree = (rootDir: Directory): void => { const printDirecory = (level: number, dir: Directory) => { console.log( `${"".padStart(level, " ")}${dir.name} - ${dir.size} - ${dir.totalSize}` ); dir.dirs.forEach((dir) => printDirecory(level + 1, dir)); }; printDirecory(0, rootDir); }; /**** PART 1 ********************************/ const solvePart1 = (data: string): number => { let rootDir: Directory | undefined = undefined; let currentDir: Directory | undefined = undefined; parseData(data).forEach((infoItem) => { if (infoItem.type === "command" && infoItem.command === "cd") { if (infoItem.name === "/") { currentDir = rootDir = new Directory({ name: "/" }); } else if (infoItem.name === "..") { if (!currentDir?.parent) { throw new Error("There is no parent directory for " + infoItem.name); } currentDir = currentDir?.parent; } else { currentDir = currentDir?.addDir(infoItem.name); } } else if (infoItem.type === "file") { if (!currentDir) { throw new Error("There is no directory to add files to"); } if (currentDir.hasFile(infoItem.name)) { return; } currentDir.files.push(infoItem.name); currentDir.size += infoItem.size; } else if (infoItem.type === "dir") { currentDir?.addDir(infoItem.name); } }); if (rootDir === undefined) { return -1; } return flatten(rootDir) .filter((dir) => dir.totalSize < 100000) .map((dir) => dir.totalSize) .reduce((sum, value) => sum + value, 0); }; console.log("Sample:", solvePart1(sample)); // 95437 console.log("Input", solvePart1(input)); //1611443 /**** PART 2 ********************************/ const solvePart2 = (data: string): number => { let rootDir: Directory | undefined = undefined; let currentDir: Directory | undefined = undefined; parseData(data).forEach((infoItem) => { if (infoItem.type === "command" && infoItem.command === "cd") { if (infoItem.name === "/") { currentDir = rootDir = new Directory({ name: "/" }); } else if (infoItem.name === "..") { if (!currentDir?.parent) { throw new Error("There is no parent directory for " + infoItem.name); } currentDir = currentDir?.parent; } else { currentDir = currentDir?.addDir(infoItem.name); } } else if (infoItem.type === "file") { if (!currentDir) { throw new Error("There is no directory to add files to"); } if (currentDir.hasFile(infoItem.name)) { return; } currentDir.files.push(infoItem.name); currentDir.size += infoItem.size; } else if (infoItem.type === "dir") { currentDir?.addDir(infoItem.name); } }); if (rootDir === undefined) { return -1; } return ( flatten(rootDir) .filter((dir) => dir.totalSize >= 2080344) .map((dir) => [dir.name, dir.totalSize] as [string, number]) .sort(([, a], [, b]) => a - b) .shift()?.[1] || -1 ); }; console.log("Sample:", solvePart2(sample)); // 24933642 console.log("Input", solvePart2(input)); //2086088