|
| 1 | +(ns advent-of-clojure.2022.07 |
| 2 | + (:require [clojure.string :as str] |
| 3 | + [clojure.walk :as walk])) |
| 4 | + |
| 5 | +(defmulti parse first) |
| 6 | + |
| 7 | +(defmethod parse \$ [line] |
| 8 | + (let [[command target] (re-seq #"\w+|\/|\.+" line)] |
| 9 | + {:type :command :command (keyword command) :target target})) |
| 10 | + |
| 11 | +(defmethod parse \d [line] |
| 12 | + (let [[_ name] (str/split line #" ")] |
| 13 | + {:type :dir :name name})) |
| 14 | + |
| 15 | +(defmethod parse :default [line] |
| 16 | + (let [[size name] (str/split line #" ")] |
| 17 | + {:type :file :size (parse-long size) :name name})) |
| 18 | + |
| 19 | +(def input (->> "resources/2022/07.dat" slurp str/split-lines (mapv parse))) |
| 20 | + |
| 21 | +(defmulti build-tree #(-> % :commands first :type)) |
| 22 | + |
| 23 | +(defn change-dir [state target] |
| 24 | + (case target |
| 25 | + "/" (assoc state :path ["/"]) |
| 26 | + ".." (update state :path pop) |
| 27 | + (update state :path conj target))) |
| 28 | + |
| 29 | +(defmethod build-tree :command [{:keys [commands] :as state}] |
| 30 | + (let [{:keys [command target]} (first commands)] |
| 31 | + (cond-> state |
| 32 | + (= command :cd) (change-dir target) |
| 33 | + :always (update :commands rest)))) |
| 34 | + |
| 35 | +(defmethod build-tree :default [{:keys [path commands] :as state}] |
| 36 | + (let [{:keys [type size name]} (first commands)] |
| 37 | + (-> state |
| 38 | + (update :tree assoc-in (conj path name) {:file/type type :file/size size}) |
| 39 | + (update :commands rest)))) |
| 40 | + |
| 41 | +(defn dir-size [tree] |
| 42 | + (for [[_ v] tree] |
| 43 | + (when (map? v) |
| 44 | + (if-let [size (:file/size v)] |
| 45 | + size |
| 46 | + (dir-size v))))) |
| 47 | + |
| 48 | +(defn dir? [node] |
| 49 | + (= :dir (get node :file/type))) |
| 50 | + |
| 51 | +(defn calculate-dir-sizes [node] |
| 52 | + (if (dir? node) |
| 53 | + (assoc node :file/size (->> node dir-size flatten (remove nil?) (apply +))) |
| 54 | + node)) |
| 55 | + |
| 56 | +(defn dir-sizes [tree] |
| 57 | + (let [sizes (atom [])] |
| 58 | + (walk/prewalk |
| 59 | + (fn [node] |
| 60 | + (when (dir? node) (swap! sizes conj (:file/size node))) |
| 61 | + node) |
| 62 | + tree) |
| 63 | + @sizes)) |
| 64 | + |
| 65 | +(defn filesystem-tree [commands] |
| 66 | + (->> {:tree {"/" {:file/type :dir}} :path [] :commands commands} |
| 67 | + (iterate build-tree) |
| 68 | + (take-while #(-> % :commands seq)) |
| 69 | + last build-tree)) |
| 70 | + |
| 71 | +(def max-size 100000) |
| 72 | + |
| 73 | +(def part-one |
| 74 | + (->> input |
| 75 | + filesystem-tree |
| 76 | + (walk/prewalk calculate-dir-sizes) |
| 77 | + dir-sizes |
| 78 | + (filter #(> max-size %)) |
| 79 | + (reduce +))) |
| 80 | + |
| 81 | +(defn find-dir-to-delete [input] |
| 82 | + (let [filesystem-tree (walk/prewalk calculate-dir-sizes (filesystem-tree input)) |
| 83 | + sizes (dir-sizes filesystem-tree) |
| 84 | + free (- 70000000 (apply max sizes))] |
| 85 | + (->> sizes |
| 86 | + (map (fn [size] [(+ free size) size])) |
| 87 | + (filter #(< 30000000 (first %))) |
| 88 | + (map second) |
| 89 | + (apply min)))) |
| 90 | + |
| 91 | +(def part-two (find-dir-to-delete input)) |
| 92 | + |
| 93 | +(comment |
| 94 | + ;; We want a tree to look like this: |
| 95 | + (def example-tree |
| 96 | + {"/" {:file/type :dir, |
| 97 | + "a" {:file/type :dir, |
| 98 | + "e" {:file/type :dir, |
| 99 | + "i" #:file{:type :file, :size 584}}, |
| 100 | + "f" #:file{:type :file, :size 29116}, |
| 101 | + "g" #:file{:type :file, :size 2557}, |
| 102 | + "h.lst" #:file{:type :file, :size 62596}}, |
| 103 | + "b.txt" #:file{:type :file, :size 14848514}, |
| 104 | + "c.dat" #:file{:type :file, :size 8504156}, |
| 105 | + "d" {:file/type :dir, |
| 106 | + "j" #:file{:type :file, :size 4060174}, |
| 107 | + "d.log" #:file{:type :file, :size 8033020}, |
| 108 | + "d.ext" #:file{:type :file, :size 5626152}, |
| 109 | + "k" #:file{:type :file, :size 7214296}}}}) |
| 110 | + ;; So that we can (get-in tree path) as path is a vector of keys. |
| 111 | + ) |
| 112 | + |
| 113 | + |
0 commit comments