|
| 1 | +(require '[clojure.string :as str] |
| 2 | + '[clojure.set :as set]) |
| 3 | + |
| 4 | +(def lines (str/split-lines (slurp "2024/15.sample2.in"))) |
| 5 | +(def the-map (pop (pop lines))) |
| 6 | +(def moves (peek lines)) |
| 7 | + |
| 8 | +(def pos->entity |
| 9 | + (into {} |
| 10 | + (for [i (range (count the-map)) |
| 11 | + j (range (count (first the-map))) |
| 12 | + :let [entity (get-in the-map [i j])] |
| 13 | + :when (#{\# \@ \O} entity)] |
| 14 | + (if (= entity \@) (do (def initial-position [i j]) nil) |
| 15 | + [[i j] entity])))) |
| 16 | + |
| 17 | +(def direction->delta {\< [0 -1], \> [0 1], \^ [-1 0], \v [1 0]}) |
| 18 | +(defn p+ [p1 p2] (mapv + p1 p2)) |
| 19 | + |
| 20 | +(defn debug [[position pos->entity]] |
| 21 | + (doseq [i (range (count the-map)) |
| 22 | + j (range (count (first the-map)))] |
| 23 | + (when (= j 0) (println)) |
| 24 | + (if (= [i j] position) |
| 25 | + (print \@) |
| 26 | + (print (or (get pos->entity [i j]) "."))))) |
| 27 | + |
| 28 | +(defn move-and-push [[position pos->entity] direction] |
| 29 | + ;; (debug [position pos->entity]) |
| 30 | + (let [delta (direction->delta direction)] |
| 31 | + (loop [looking-at (p+ position delta), seen-box false] |
| 32 | + (case (get pos->entity looking-at) |
| 33 | + \O (recur (p+ looking-at delta) true) |
| 34 | + \# [position pos->entity] |
| 35 | + [(p+ position delta) |
| 36 | + (if seen-box |
| 37 | + (-> pos->entity |
| 38 | + (dissoc (p+ position delta)) |
| 39 | + (assoc looking-at \O)) |
| 40 | + pos->entity)])))) |
| 41 | + |
| 42 | +(defn gps-sum [[_ pos->entity]] |
| 43 | + (reduce + (keep (fn [[[i j] c]] (when (= c \O) (+ (* 100 i) j))) pos->entity))) |
| 44 | + |
| 45 | +(gps-sum (reduce move-and-push [initial-position pos->entity] moves)) |
| 46 | + |
| 47 | +(defn box-at [boxes [i j]] (or (boxes [i j]) (boxes [i (dec j)]))) |
| 48 | + |
| 49 | +(let [*walls (transient #{}), *boxes (transient #{})] |
| 50 | + (doseq [i (range (count the-map)) |
| 51 | + j (range (count (first the-map))) |
| 52 | + :let [entity (get-in the-map [i j])] |
| 53 | + :when (#{\# \@ \O} entity)] |
| 54 | + (case entity |
| 55 | + \O (conj! *boxes [i (* j 2)]) |
| 56 | + \# (-> *walls |
| 57 | + (conj! [i (* j 2)]) |
| 58 | + (conj! [i (inc (* j 2))])) |
| 59 | + \@ (def initial-position [i (* j 2)]))) |
| 60 | + (def walls (persistent! *walls)) |
| 61 | + (def boxes (persistent! *boxes))) |
| 62 | + |
| 63 | +(defn debug' [[position boxes]] |
| 64 | + (doseq [i (range (count the-map)) |
| 65 | + j (range (* (count (first the-map)) 2))] |
| 66 | + (when (= j 0) (println)) |
| 67 | + (cond |
| 68 | + (= [i j] (box-at boxes [i j])) |
| 69 | + (print \[) |
| 70 | + |
| 71 | + (= [i (dec j)] (box-at boxes [i j])) |
| 72 | + (print \]) |
| 73 | + |
| 74 | + (walls [i j]) |
| 75 | + (print "#") |
| 76 | + |
| 77 | + (= [i j] position) |
| 78 | + (print "@") |
| 79 | + |
| 80 | + :else |
| 81 | + (print " ")))) |
| 82 | + |
| 83 | +(defn move-and-push' [[position boxes] direction] |
| 84 | + (debug' [position boxes]) |
| 85 | + (let [delta (direction->delta direction) |
| 86 | + front (p+ position delta)] |
| 87 | + (loop [positions-to-check #{front} |
| 88 | + boxes-seen #{} |
| 89 | + visited #{}] |
| 90 | + (let [checking-position (first positions-to-check) |
| 91 | + box-at-position (some->> checking-position (box-at boxes)) |
| 92 | + visit-unvisited |
| 93 | + (fn [positions-to-check position] |
| 94 | + (if ((conj visited checking-position) position) |
| 95 | + positions-to-check |
| 96 | + (conj positions-to-check position)))] |
| 97 | + (cond |
| 98 | + (nil? checking-position) |
| 99 | + [front |
| 100 | + (-> boxes |
| 101 | + (set/difference boxes-seen) |
| 102 | + (into (map (partial p+ delta) boxes-seen)))] |
| 103 | + |
| 104 | + (contains? walls checking-position) |
| 105 | + [position boxes] |
| 106 | + |
| 107 | + box-at-position |
| 108 | + (recur (-> positions-to-check |
| 109 | + (disj checking-position) |
| 110 | + (visit-unvisited box-at-position) |
| 111 | + (visit-unvisited (p+ [0 1] box-at-position)) |
| 112 | + (visit-unvisited (p+ delta checking-position))) |
| 113 | + (cond-> boxes-seen box-at-position (conj box-at-position)) |
| 114 | + (conj visited checking-position)) |
| 115 | + |
| 116 | + :else |
| 117 | + (recur (disj positions-to-check checking-position) |
| 118 | + boxes-seen |
| 119 | + (conj visited checking-position))))))) |
| 120 | + |
| 121 | +(defn gps-sum' [[_ boxes]] |
| 122 | + (reduce + (map (fn [[i j]] (+ (* 100 i) j)) boxes))) |
| 123 | + |
| 124 | +(gps-sum' (reduce move-and-push' [initial-position boxes] moves)) |
0 commit comments