192 lines
5.2 KiB
TypeScript
192 lines
5.2 KiB
TypeScript
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
|