|
| 1 | +(ns y2024.d18 |
| 2 | + (:require |
| 3 | + [advent-of-code-clj.input :as input] |
| 4 | + [advent-of-code-clj.utils :as utils] |
| 5 | + [clojure.core.matrix :as mx] |
| 6 | + [ubergraph.alg :as ua])) |
| 7 | + |
| 8 | +; # 2024, dag 18 |
| 9 | + |
| 10 | +; ## Del 1 |
| 11 | + |
| 12 | +; Test-inputten inneholder koordinater for noder som blokkerer en path fra [0 0] til [6 6]. |
| 13 | +; Her kan vi bruke `ubergraph` sin shortest-path-algoritme, men først må vi hente ut riktig |
| 14 | +; antall blokkerende noder. |
| 15 | + |
| 16 | +(def test-input "5,4 |
| 17 | +4,2 |
| 18 | +4,5 |
| 19 | +3,0 |
| 20 | +2,1 |
| 21 | +6,3 |
| 22 | +2,4 |
| 23 | +1,5 |
| 24 | +0,6 |
| 25 | +3,3 |
| 26 | +2,6 |
| 27 | +5,1 |
| 28 | +1,2 |
| 29 | +5,5 |
| 30 | +2,5 |
| 31 | +6,5 |
| 32 | +1,4 |
| 33 | +0,4 |
| 34 | +6,4 |
| 35 | +1,1 |
| 36 | +6,1 |
| 37 | +1,0 |
| 38 | +0,5 |
| 39 | +1,6 |
| 40 | +2,0") |
| 41 | + |
| 42 | +; I parsingen reverserer vi X og Y for å bedre passe inn i matrisen vår. |
| 43 | + |
| 44 | +(defn corrupted-locations [input] |
| 45 | + (->> input |
| 46 | + (re-seq #"\d+") |
| 47 | + (map parse-long) |
| 48 | + (partition 2) |
| 49 | + (map reverse) |
| 50 | + (map vec))) |
| 51 | + |
| 52 | +(corrupted-locations test-input) |
| 53 | + |
| 54 | +; Pathen kan finnes ved å hente nabo-noder og filtrere vekk noder som er "out of bounds", |
| 55 | +; og som er blokkert av `corrupted-locations`: |
| 56 | + |
| 57 | +(defn find-path [[dimy dimx :as _dimensions] corrupted-locations] |
| 58 | + (let [in-bounds? (fn [[y x]] |
| 59 | + (and (>= dimy y 0) |
| 60 | + (>= dimx x 0)))] |
| 61 | + (ua/shortest-path |
| 62 | + (fn [node] |
| 63 | + (->> (apply utils/adjacent-hv node) |
| 64 | + (remove (some-fn corrupted-locations |
| 65 | + (complement in-bounds?))) |
| 66 | + (map (fn [x] {:dest x})))) |
| 67 | + [0 0] [dimy dimx]))) |
| 68 | + |
| 69 | +; Viktig å huske på at vi ikke skal ta inn alle nodene i input, så funksjonen vår kan |
| 70 | +; ta to argumenter for dimensjon og antall "bytes": |
| 71 | + |
| 72 | +(defn part-1 [dimensions bytes input] |
| 73 | + (let [corrupted-locations (set (take bytes (corrupted-locations input)))] |
| 74 | + (:cost (find-path dimensions corrupted-locations)))) |
| 75 | + |
| 76 | +; Det gir oss følgende svar for del 1: |
| 77 | + |
| 78 | +(delay (part-1 [6 6] 12 test-input)) |
| 79 | + |
| 80 | +(delay (part-1 [70 70] 1024 (input/get-input 2024 18))) |
| 81 | + |
| 82 | +; For debugging har vi denne hjelpemetoden som printer ut matrisen: |
| 83 | + |
| 84 | +(defn print-matrix [[y x :as dims] bytes input] |
| 85 | + (let [corrupted-nodes (set (take bytes (corrupted-locations input))) |
| 86 | + travel-path (set (ua/nodes-in-path (find-path dims corrupted-nodes)))] |
| 87 | + ^:kind/hiccup |
| 88 | + [:pre |
| 89 | + (with-out-str |
| 90 | + (mx/pm (mx/emap-indexed (fn [node _] |
| 91 | + (cond |
| 92 | + (corrupted-nodes node) \# |
| 93 | + (travel-path node) \O |
| 94 | + :else \.)) |
| 95 | + (mx/new-matrix (inc y) (inc x)))))])) |
| 96 | + |
| 97 | +(delay (print-matrix [6 6] 12 test-input)) |
| 98 | + |
| 99 | +; ## Del 2 |
| 100 | + |
| 101 | +; I del 2 må vi finne ut hvilken node som vil føre til at det ikke er mulig å finne en |
| 102 | +; gyldig path til målet. |
| 103 | + |
| 104 | +; Dette løser vi ved å snevre inn søkeområdet basert på om vi finner en gyldig path på |
| 105 | +; starten, midten og slutten av søkeområdet. |
| 106 | + |
| 107 | +; (*TODO: denne algoritmen trenger litt refaktorering*) |
| 108 | + |
| 109 | +(defn part-2 [dimensions input] |
| 110 | + (let [num-bytes (alength (.split input "\n")) |
| 111 | + corrupted-locations (corrupted-locations input)] |
| 112 | + (->> (nth corrupted-locations |
| 113 | + (loop [[start end] [0 num-bytes]] |
| 114 | + (if (>= 1 (- end start)) |
| 115 | + (first (filter (fn [x] |
| 116 | + (find-path dimensions (set (take x corrupted-locations)))) |
| 117 | + [start end])) |
| 118 | + (let [middle (int (+ (/ (- end start) 2) start)) |
| 119 | + corrupted-locations-start (set (take start corrupted-locations)) |
| 120 | + corrupted-locations-middle (set (take middle corrupted-locations)) |
| 121 | + corrupted-locations-end (set (take end corrupted-locations)) |
| 122 | + res-start (find-path dimensions corrupted-locations-start) |
| 123 | + res-middle (find-path dimensions corrupted-locations-middle) |
| 124 | + res-end (find-path dimensions corrupted-locations-end)] |
| 125 | + (cond |
| 126 | + (= nil res-end res-middle) (recur [start middle]) |
| 127 | + (and res-start res-middle) (recur [middle end])))))) |
| 128 | + reverse |
| 129 | + (map str) |
| 130 | + (String/join ",")))) |
| 131 | + |
| 132 | +(delay (part-2 [6 6] test-input)) |
| 133 | +(delay (part-2 [70 70] (input/get-input 2024 18))) |
0 commit comments