1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
import fs from "fs";
const file = fs.readFileSync("./input.txt").toString();
const lines = file.split("\n").filter((x) => x.length);
// File or Directory
interface Item {
type: "dir" | "file";
name: string;
size: number;
}
interface CommandWithOutput {
command: string;
outputs: Item[];
}
interface CommandWithDir extends CommandWithOutput {
directory: string[];
}
function isCommandString(s: string) {
return s.startsWith("$");
}
function parseLines(lines: string[]): CommandWithOutput[] {
return lines.reduce((acc, current, index) => {
if (isCommandString(current)) {
const nextCommandIndex =
lines.slice(index + 1).findIndex(isCommandString) + index + 1;
const smartIndex =
nextCommandIndex === index ? lines.length : nextCommandIndex;
const outputs = lines.slice(index + 1, smartIndex).map((line) => {
const dirMatch = line.match(/dir (.*)/);
const fileMatch = line.match(/(.*) (.*)/);
if (dirMatch && dirMatch[1])
return {
type: "dir",
size: 0,
name: dirMatch[1],
} satisfies Item;
if (fileMatch)
return {
type: "file",
size: Number(fileMatch[1]),
name: fileMatch[2],
} satisfies Item;
throw new Error("Unhandled");
});
acc.push({ outputs, command: current });
}
return acc;
}, [] as CommandWithOutput[]);
}
function computeDirectories(commands: CommandWithOutput[]): CommandWithDir[] {
return commands.reduce((acc, current) => {
const previousDir = acc[acc.length - 1]?.directory;
const match = current.command.match(/\$ cd (.*)/);
const directory = match
? previousDir
? match[1] !== ".."
? [...previousDir, match[1]]
: [...previousDir.slice(0, -1)]
: [match[1]]
: [...previousDir];
acc.push({ ...current, directory });
return acc;
}, [] as CommandWithDir[]);
}
const parsed = parseLines(lines);
const withDirs = computeDirectories(parsed);
const lsOutputs = withDirs.filter((cmd) => cmd.command === "$ ls");
// DIRTY CODE STARTS HERE
function computeDirSize(
cmd: CommandWithDir & { totalSize: number | undefined },
) {
if (cmd.totalSize !== undefined) return;
cmd.outputs
.filter((f) => f.type === "dir")
.forEach((dir) => {
const path = [...cmd.directory, dir.name];
const dirNode = data.find(
(datum) => JSON.stringify(datum.directory) === JSON.stringify(path),
);
if (!dirNode) throw new Error(`Unknown size ${dir.name}`);
if (dirNode.totalSize === undefined) {
computeDirSize(dirNode);
}
if (dirNode.totalSize !== undefined) {
dir.size = dirNode.totalSize;
} else {
throw new Error(
`${dir.name}, ${JSON.stringify(path)}, ${JSON.stringify(dirNode)}`,
);
}
});
const totalSize = cmd.outputs.reduce((sum, f) => sum + f.size, 0);
cmd.totalSize = totalSize;
// console.log("Setting size ", totalSize, " to dir", cmd.directory);
}
const data = lsOutputs.map((o) => ({
...o,
totalSize: undefined as undefined | number,
}));
data.forEach(computeDirSize);
console.log(data);
const sum = data
.filter((d) => d.totalSize)
.filter((datum) => (datum?.totalSize || 0) <= 100_000)
.reduce((acc, d) => acc + (d?.totalSize || 0), 0);
console.log(sum);
|