|
1 | 1 | defmodule AdventOfCode.Day07 do
|
2 |
| - defmodule Folder do |
3 |
| - @enforce_keys [:name] |
4 |
| - defstruct [:name, files: %{}, folders: %{}, type: :dir] |
5 |
| - end |
6 |
| - |
7 |
| - defmodule File do |
8 |
| - @enforce_keys [:name, :size] |
9 |
| - defstruct [:name, :size, type: :file] |
10 |
| - end |
11 |
| - |
12 |
| - @doc""" |
13 |
| - defimpl Inspect, for: Folder do |
14 |
| - def inspect(folder, _opts) do |
15 |
| - folderText = "" # folder.folders.keys() |
16 |
| - fileText = "|- a file\n" |
17 |
| - "-- {folder.name}\n" <> folderText <> fileText |
18 |
| - end |
19 |
| - end |
20 |
| -
|
21 |
| - defimpl Inspect, for: File do |
22 |
| - def inspect(file, _opts) do |
23 |
| - "-- {file.size} {file.name}\n" |
24 |
| - end |
25 |
| - end |
26 |
| - """ |
27 |
| - defprotocol FileTree do |
28 |
| - def size(value) |
29 |
| - end |
30 |
| - |
31 |
| - defimpl FileTree, for: Folder do |
32 |
| - def size(dir), do: |
33 |
| - dir.content |
34 |
| - |> Enum.map(&FileTree.size/1) |
35 |
| - |> Enum.sum() |
36 |
| - end |
37 |
| - |
38 |
| - defimpl FileTree, for: File do |
39 |
| - def size(file), do: file.size |
40 |
| - end |
41 |
| - |
42 | 2 | @spec parseCommand(String.t()) :: list()
|
43 | 3 | def parseCommand(rawStr) do
|
44 | 4 | cond do
|
45 |
| - String.starts_with?(rawStr, "$ ls") -> [:ls] |
46 |
| - String.starts_with?(rawStr, "$ cd") -> [:cd, String.trim_leading(rawStr, "$ cd ")] |
47 |
| - String.starts_with?(rawStr, "dir") -> [:dir, String.trim_leading(rawStr, "dir ")] |
| 5 | + String.starts_with?(rawStr, "$ ls") -> |
| 6 | + [:ls] |
| 7 | + |
| 8 | + String.starts_with?(rawStr, "$ cd") -> |
| 9 | + [:cd, String.trim_leading(rawStr, "$ cd ")] |
| 10 | + |
| 11 | + String.starts_with?(rawStr, "dir") -> |
| 12 | + [:dir, String.trim_leading(rawStr, "dir ")] |
| 13 | + |
48 | 14 | String.match?(rawStr, ~r/^\d+ .+$/) ->
|
49 | 15 | [size, name] = String.split(rawStr, " ")
|
50 | 16 | [:file, String.to_integer(size), name]
|
51 |
| - true -> [:unknown, rawStr] |
| 17 | + |
| 18 | + true -> |
| 19 | + [:unknown, rawStr] |
52 | 20 | end
|
53 | 21 | end
|
54 | 22 |
|
55 |
| - @spec executeCommand(list(), list()) :: %Folder{} |
56 |
| - def executeCommand([:cd, "/"], [_path, rootNode]), do: [["/"], rootNode] |
57 |
| - def executeCommand([:cd, ".."], [["/"], rootNode]), do: [["/"], rootNode] |
58 |
| - def executeCommand([:cd, ".."], [path, rootNode]), do: [Enum.drop(path, -1), rootNode] |
59 |
| - def executeCommand([:cd, folder], [path, rootNode]) do |
60 |
| - # nextNode = Enum.find(currentNode.content, fn x -> x.type == :dir && x.name == folder end) |
61 |
| - [ path ++ [folder], rootNode] |
62 |
| - end |
63 |
| - def executeCommand([:ls], [path, rootNode]), do: [path, rootNode] |
64 |
| - def executeCommand([:file, size, name], [path, rootNode]) |
65 |
| - do |
66 |
| - newFile = %File{name: name, size: size} |
67 |
| - newTree = addItemToContent(path, rootNode, newFile) |
68 |
| - [path, newTree] |
69 |
| - end |
70 |
| - def executeCommand([:dir, name], [path, rootNode]) |
71 |
| - do |
72 |
| - newDir = %Folder{name: name} |
73 |
| - newTree = addItemToContent(path, rootNode, newDir) |
74 |
| - [path, newTree] |
75 |
| - end |
| 23 | + def executeCommand([:cd, "/"], [_path, rootNode, folders]), |
| 24 | + do: [["/"], rootNode, folders] |
76 | 25 |
|
77 |
| - @spec addItemToContent(list(), %Folder{}, %Folder{} | %File{}) :: %Folder{} |
78 |
| - def addItemToContent(path, root, item) do |
79 |
| - access_path = transformToAccessPath(path) |
80 |
| - dir = if access_path == [], do: root, else: get_in(root, access_path) |
81 |
| - IO.inspect(dir) |
82 |
| - if duplicate?(dir, item) |
83 |
| - do |
84 |
| - root |
85 |
| - else |
86 |
| - # get_and_update_in(root, [Access.key(:files), Access.key(:abaa), Access.key(:size)], &{&1, &1 + 1000}) |
87 |
| - #get_and_update_in(root, path, fn currentFolder -> %{currentFolder | content: Map.update(dir.content, item.name, item)} end) |
88 |
| - if access_path != [] |
89 |
| - do |
90 |
| - update_in(root, access_path, &(Map.put(&1, item.name, item))) |
91 |
| - else |
92 |
| - key = if item.type == :dir, do: :folders, else: :files |
93 |
| - # update(root, ) |
94 |
| - end |
95 |
| - end |
96 |
| - end |
| 26 | + def executeCommand([:cd, ".."], [["/"], rootNode, folders]), |
| 27 | + do: [["/"], rootNode, folders] |
97 | 28 |
|
98 |
| - def transformToAccessPath(path) do |
99 |
| - path |> Enum.map(&Access.key/1) |> Enum.intersperse(Access.key(:folders)) |> Enum.drop(1) |
100 |
| - end |
| 29 | + def executeCommand([:cd, ".."], [path, rootNode, folders]), |
| 30 | + do: [Enum.drop(path, -1), rootNode, folders] |
101 | 31 |
|
102 |
| - @spec duplicate?(%Folder{}, %Folder{} | %File{}) :: boolean() |
103 |
| - def duplicate?(folder, item) do |
104 |
| - key = if item.type == :dir, do: :folders, else: :files |
105 |
| - get_in(folder, [Access.key(key)]) |> Map.keys() |> Enum.member?(item.name) |
106 |
| - end |
| 32 | + def executeCommand([:cd, folder], [path, rootNode, folders]), |
| 33 | + do: [path ++ [folder], rootNode, MapSet.put(folders, path ++ [folder])] |
| 34 | + |
| 35 | + def executeCommand([:ls], [path, rootNode, folders]), |
| 36 | + do: [path, rootNode, folders] |
| 37 | + |
| 38 | + def executeCommand([:file, size, name], [path, rootNode, folders]), |
| 39 | + do: [path, update_in(rootNode, path, &Map.put(&1, name, size)), folders] |
| 40 | + |
| 41 | + def executeCommand([:dir, name], [path, rootNode, folders]), |
| 42 | + do: [path, update_in(rootNode, path, &Map.put(&1, name, %{})), folders] |
107 | 43 |
|
108 | 44 | def part1(args) do
|
109 |
| - root = %Folder{name: "/"} |
| 45 | + root = %{"/" => %{}} |
110 | 46 |
|
111 |
| - tree = args |
112 |
| - |> String.trim() |
113 |
| - |> String.split("\n") |
114 |
| - |> Enum.map(&parseCommand/1) |
115 |
| - |> Enum.reduce(["/", root], fn x, acc -> executeCommand(x, acc) end) |
| 47 | + [_, tree, visitedPaths] = |
| 48 | + args |
| 49 | + |> String.trim() |
| 50 | + |> String.split("\n") |
| 51 | + |> Enum.map(&parseCommand/1) |
| 52 | + |> Enum.reduce(["/", root, MapSet.new([["/"]])], fn x, acc -> executeCommand(x, acc) end) |
116 | 53 |
|
117 |
| - IO.inspect(tree) |
| 54 | + visitedPaths |
| 55 | + |> Enum.map(&(get_in(tree, &1) |> getSize())) |
| 56 | + |> Enum.filter(&(&1 <= 100_000)) |
| 57 | + |> Enum.sum() |
118 | 58 | end
|
119 | 59 |
|
120 |
| - def part2(_args) do |
| 60 | + def part2(args) do |
| 61 | + root = %{"/" => %{}} |
| 62 | + |
| 63 | + [_, tree, visitedPaths] = |
| 64 | + args |
| 65 | + |> String.trim() |
| 66 | + |> String.split("\n") |
| 67 | + |> Enum.map(&parseCommand/1) |
| 68 | + |> Enum.reduce(["/", root, MapSet.new([["/"]])], fn x, acc -> executeCommand(x, acc) end) |
| 69 | + |
| 70 | + otherDirectories = MapSet.delete(visitedPaths, [["/"]]) |
| 71 | + |
| 72 | + space_available = 70_000_000 - getSize(get_in(tree, ["/"])) |
| 73 | + additional_space_needed = 30_000_000 - space_available |
| 74 | + |
| 75 | + directorySizes = |
| 76 | + otherDirectories |
| 77 | + |> Enum.map(&(get_in(tree, &1) |> getSize())) |
| 78 | + |> Enum.sort() |
| 79 | + |> Enum.filter(&(&1 >= additional_space_needed)) |
| 80 | + |> hd |
121 | 81 | end
|
122 | 82 |
|
123 | 83 | @spec getSize(list) :: number
|
124 | 84 | @doc """
|
125 | 85 | Returns the size of a filetree in form of a Map.
|
126 | 86 | """
|
127 |
| - def getSize(tree) do |
128 |
| - tree |> Map.to_list() |> List.flatten() |> Enum.map(&getSize/1) |> Enum.sum() |
| 87 | + def getSize(element) when is_integer(element), do: element |
| 88 | + |
| 89 | + def getSize(tuple) when is_tuple(tuple) and tuple_size(tuple) == 2 do |
| 90 | + {_, map} = tuple |
| 91 | + getSize(map) |
| 92 | + end |
| 93 | + |
| 94 | + def getSize(map) when is_map(map) do |
| 95 | + map |> Map.to_list() |> List.flatten() |> Enum.map(&getSize/1) |> Enum.sum() |
129 | 96 | end
|
130 | 97 | end
|
0 commit comments