summaryrefslogtreecommitdiff
path: root/day-7/index.ts
blob: 689e647886c0fdf9aaae6111ca0cabe79cd4ae09 (plain)
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);