Skip to content

Instantly share code, notes, and snippets.

@quephird
Last active December 8, 2022 00:22
Show Gist options
  • Save quephird/f4906fdccee0d9ff03e92acf3f55bdb7 to your computer and use it in GitHub Desktop.
Save quephird/f4906fdccee0d9ff03e92acf3f55bdb7 to your computer and use it in GitHub Desktop.
(require ['clojure.string :as 'str])
;; The file system is represented by a deep hashmap
;; of file and directory names as keys, and either a
;; file size or list of subdirectory names and files as
;; their values.
(def file-system (atom {"/" {}}))
;; The current directory is always a vector of names.
;; For example, the path "/foo/bar/baz" is represented by
;; the vector ["/" "foo" "bar" "baz"].
(def current-directory (atom []))
(defn handle-cd [arg]
(cond
(= "/" arg)
(reset! current-directory ["/"])
(= ".." arg)
(swap! current-directory #(-> % drop-last vec))
:else
(swap! current-directory conj arg)))
(defn handle-ls []
;; This is effectively a no-op but is here for clarity.
nil)
(defn handle-command [command arg]
(cond
(= "cd" command)
(handle-cd arg)
:else
(handle-ls)))
(defn handle-create-directory [new-dir]
(swap! file-system assoc-in (conj @current-directory new-dir) {}))
(defn handle-create-file [new-file size]
(swap! file-system assoc-in (conj @current-directory new-file) (Integer/parseInt size)))
(defn handle-command-output [a b]
(cond
(= "dir" a)
(handle-create-directory b)
:else
(handle-create-file b a)))
(defn parse-line [line]
(let [[a b c] (str/split line #" ")]
(cond
(= "$" a)
(handle-command b c)
:else
(handle-command-output a b))))
(defn parse-file [lines]
(loop [[line & rest] lines]
(if (empty? line)
nil
(do
(parse-line line)
(recur rest)))))
(defn directory? [path]
(map? (get-in @file-system path)))
(defn file? [path]
(int? (get-in @file-system path)))
(defn contents-paths [dir-path]
(->> dir-path
(get-in @file-system)
keys
(map #(conj dir-path %))))
(defn subdirectories [path]
(if (file? path)
nil
(->> path
contents-paths
(filter directory?))))
(defn all-directory-paths [starting-path]
(let [subdirs (subdirectories starting-path)]
(apply concat [starting-path] (map all-directory-paths subdirs))))
(defn size [path]
(if (file? path)
(get-in @file-system path)
(let [contents (get-in @file-system path)]
(->> (keys contents)
(map #(conj path %))
(map size)
(apply +)))))
(defn load-input [filename]
(->> filename
slurp
str/split-lines
parse-file))
(defn solution-part-1 []
(->> ["/"]
all-directory-paths
(map size)
(filter #(<= % 100000))
(apply +)))
(defn solution-part-2 []
(let [used-space (size ["/"])
unused-space (- 70000000 used-space)
min-space-to-recover (- 30000000 unused-space)]
(->> ["/"]
all-directory-paths
(map size)
(filter #(>= % min-space-to-recover))
(apply min))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment