|
| 1 | +(ns day21 |
| 2 | + (:require [clojure.set :as set] |
| 3 | + [clojure.string :as str] |
| 4 | + [clojure.java.io :as io])) |
| 5 | + |
| 6 | +(defn myprint [x] (println x) x) |
| 7 | + |
| 8 | +(def numpad-neighbors |
| 9 | + {\7 [[:right \8] [:down \4]] |
| 10 | + \8 [[:left \7] [:right \9] [:down \5]] |
| 11 | + \9 [[:left \8] [:down \6]] |
| 12 | + \4 [[:up \7] [:right \5] [:down \1]] |
| 13 | + \5 [[:up \8] [:right \6] [:down \2] [:left \4]] |
| 14 | + \6 [[:up \9] [:left \5] [:down \3]] |
| 15 | + \1 [[:up \4] [:right \2]] |
| 16 | + \2 [[:up \5] [:right \3] [:down \0] [:left \1]] |
| 17 | + \3 [[:left \2] [:up \6] [:down \A]] |
| 18 | + \0 [[:up \2] [:right \A]] |
| 19 | + \A [[:left \0] [:up \3]]}) |
| 20 | + |
| 21 | +(def arrowpad-neighbors |
| 22 | + {\A [[:left \^] [:down \>]] |
| 23 | + \^ [[:right \A] [:down \v]] |
| 24 | + \< [[:right \v]] |
| 25 | + \v [[:left \<] [:up \^] [:right \>]] |
| 26 | + \> [[:left \v] [:up \A]]}) |
| 27 | + |
| 28 | +(defn read-codes [filename] |
| 29 | + (with-open [file (io/reader filename)] |
| 30 | + (into [] (line-seq file)))) |
| 31 | + |
| 32 | +(defn translate [code] |
| 33 | + (let [mapper {:up \^, :down \v, :left \<, :right \>, \A \A}] |
| 34 | + (str/join (map mapper code)))) |
| 35 | + |
| 36 | +; Ordering of the directions is critical to identifying the shortest path |
| 37 | +(def directions [:left :down :up :right]) |
| 38 | + |
| 39 | +(defn gen-consecutive-pairs [lst] (map vector (drop-last 1 lst) (drop 1 lst))) |
| 40 | + |
| 41 | +(defn get-movements [neighbors] |
| 42 | + (defn get-acyclic-paths [[path & remaining] paths] |
| 43 | + (if (nil? path) |
| 44 | + paths |
| 45 | + (let [last-node (last path) |
| 46 | + next-nodes (filter #(not (.contains path (second %))) |
| 47 | + (neighbors last-node))] |
| 48 | + (get-acyclic-paths (concat remaining (map #(concat path %) next-nodes)) |
| 49 | + (conj paths path))))) |
| 50 | + |
| 51 | + (defn get-min-path-between [[src-trg paths]] |
| 52 | + (defn key-helper [path] |
| 53 | + (reduce (fn [acc v] (+ (* acc 10) (.indexOf directions v))) 0 path)) |
| 54 | + (defn is-one-turn-path? [path] |
| 55 | + (let [consecutive-dirs (gen-consecutive-pairs path) |
| 56 | + turns (filter #(not= (first %) (second %)) consecutive-dirs)] |
| 57 | + (<= (count turns) 1))) |
| 58 | + (let [dir-only-paths (map (fn [p] (filter #(.contains directions %) p)) |
| 59 | + paths) |
| 60 | + one-turn-paths (filter #(is-one-turn-path? %) dir-only-paths)] |
| 61 | + [src-trg (apply min-key key-helper one-turn-paths)])) |
| 62 | + |
| 63 | + (defn get-min-paths [acc src] |
| 64 | + (->> |
| 65 | + (get-acyclic-paths [[src]] #{}) |
| 66 | + (group-by (fn [x] [src (last x)])) |
| 67 | + (map get-min-path-between) |
| 68 | + (into acc))) |
| 69 | + (reduce get-min-paths {} (keys neighbors))) |
| 70 | + |
| 71 | +(defn translate-code-to-moves [src-trg-pairs from-to-moves moves] |
| 72 | + (translate (flatten (map #(concat (from-to-moves %) '(\A)) src-trg-pairs)))) |
| 73 | + |
| 74 | +(defn get-length-of-expanded-code [code reps arrowpad-from-to-moves] |
| 75 | + (defn helper [code-segment depth memory] |
| 76 | + (cond |
| 77 | + (contains? memory [code-segment depth]) memory |
| 78 | + (== depth reps) (assoc memory [code-segment depth] |
| 79 | + (- (count code-segment) 1)) |
| 80 | + :else (let [new-code-segments (->> |
| 81 | + code-segment |
| 82 | + (gen-consecutive-pairs) |
| 83 | + (map arrowpad-from-to-moves) |
| 84 | + (map #(str "A" (translate %) "A"))) |
| 85 | + next-depth (+ 1 depth) |
| 86 | + new-memory (reduce #(helper %2 next-depth %) memory |
| 87 | + new-code-segments) |
| 88 | + length (reduce + (map #(new-memory [% next-depth]) |
| 89 | + new-code-segments))] |
| 90 | + (assoc new-memory [code-segment depth] length)))) |
| 91 | + |
| 92 | + (let [code-segments (map #(str "A" % "A") (str/split code #"A")) |
| 93 | + memory (reduce #(helper %2 0 %1) {} code-segments)] |
| 94 | + (reduce + (map #(memory [% 0]) code-segments)))) |
| 95 | + |
| 96 | +(defn solve [codes numpad-from-to-moves arrowpad-from-to-moves num-of-arrowpads] |
| 97 | + (defn type-code-on-numpad [code] |
| 98 | + (->> |
| 99 | + (gen-consecutive-pairs (cons \A (seq code))) |
| 100 | + (map #(concat (numpad-from-to-moves %) '(\A))) |
| 101 | + (flatten) |
| 102 | + (translate))) |
| 103 | + |
| 104 | + (defn process-code [code] |
| 105 | + (let [numpad-moves (type-code-on-numpad code) |
| 106 | + moves-length (get-length-of-expanded-code numpad-moves |
| 107 | + num-of-arrowpads |
| 108 | + arrowpad-from-to-moves) |
| 109 | + code-value (Integer. (str/replace code #"A" ""))] |
| 110 | + (* code-value moves-length))) |
| 111 | + (reduce + (map process-code codes))) |
| 112 | + |
| 113 | +(defn -main [filename num-of-robots] |
| 114 | + (let [codes (read-codes filename) |
| 115 | + numpad-from-to-moves (get-movements numpad-neighbors) |
| 116 | + arrowpad-from-to-moves (get-movements arrowpad-neighbors)] |
| 117 | + (println (solve codes numpad-from-to-moves arrowpad-from-to-moves |
| 118 | + (Integer. num-of-robots))))) |
0 commit comments