You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
99 lines
3.7 KiB
Clojure
99 lines
3.7 KiB
Clojure
3 years ago
|
(require '[clojure.core.reducers :as r])
|
||
|
(require 'clojure.string)
|
||
|
|
||
3 years ago
|
(def orientation-vectors
|
||
|
[[[1 0 0] [0 1 0] [0 0 1]]
|
||
|
[[1 0 0] [0 0 -1] [0 1 0]]
|
||
|
[[1 0 0] [0 -1 0] [0 0 -1]]
|
||
|
[[1 0 0] [0 0 1] [0 -1 0]]
|
||
|
[[-1 0 0] [0 -1 0] [0 0 1]]
|
||
|
[[-1 0 0] [0 0 -1] [0 -1 0]]
|
||
|
[[-1 0 0] [0 1 0] [0 0 -1]]
|
||
|
[[-1 0 0] [0 0 1] [0 1 0]]
|
||
|
[[0 0 -1] [1 0 0] [0 -1 0]]
|
||
|
[[0 1 0] [1 0 0] [0 0 -1]]
|
||
|
[[0 0 1] [1 0 0] [0 1 0]]
|
||
|
[[0 -1 0] [1 0 0] [0 0 1]]
|
||
|
[[0 0 1] [-1 0 0] [0 -1 0]]
|
||
|
[[0 1 0] [-1 0 0] [0 0 1]]
|
||
|
[[0 0 -1] [-1 0 0] [0 1 0]]
|
||
|
[[0 -1 0] [-1 0 0] [0 0 -1]]
|
||
|
[[0 -1 0] [0 0 -1] [1 0 0]]
|
||
|
[[0 0 1] [0 -1 0] [1 0 0]]
|
||
|
[[0 1 0] [0 0 1] [1 0 0]]
|
||
|
[[0 0 -1] [0 1 0] [1 0 0]]
|
||
|
[[0 0 1] [0 1 0] [-1 0 0]]
|
||
|
[[0 -1 0] [0 0 1] [-1 0 0]]
|
||
|
[[0 0 -1] [0 -1 0] [-1 0 0]]
|
||
|
[[0 1 0] [0 0 -1] [-1 0 0]]])
|
||
|
|
||
3 years ago
|
(defn build-scanner-reports
|
||
3 years ago
|
"Splits slurped, split-line'd input by scanner."
|
||
3 years ago
|
[input-lines]
|
||
|
(loop [scanners [] rem-input input-lines]
|
||
|
(if (empty? rem-input)
|
||
|
scanners
|
||
|
(let [[this nxt] (split-with (comp not empty?) rem-input)]
|
||
3 years ago
|
(recur (->> (rest this)
|
||
|
(map #(read-string (clojure.string/join ["[" % "]"])))
|
||
|
(conj scanners))
|
||
|
(rest nxt))))))
|
||
3 years ago
|
|
||
3 years ago
|
(defn point-add [[a b c] [d e f]] [(+ a d) (+ b e) (+ c f)])
|
||
3 years ago
|
|
||
|
(defn orient [[x y z] [[a b c] [d e f] [g h i]]]
|
||
|
[(+ (* x a) (* y d) (* z g)) (+ (* x b) (* y e) (* z h)) (+ (* x c) (* y f) (* z i))])
|
||
|
|
||
|
(defn scanner-orientation [report or-index]
|
||
3 years ago
|
(map #(orient (map - %) (get orientation-vectors or-index)) report))
|
||
3 years ago
|
|
||
|
(defn scanner-orientations
|
||
|
"Builds list of scanner reports adjusted for all possible orientations."
|
||
|
[report]
|
||
3 years ago
|
(for [or-vec orientation-vectors]
|
||
3 years ago
|
(map #(orient (map - %) or-vec) report)))
|
||
|
|
||
|
(defn scanner-build-potential-links
|
||
|
"Builds a list for each s2 orientation that contains lists of each s1 point
|
||
|
added to the corresponding s2 point."
|
||
|
[s1 s2]
|
||
|
(for [s2-or (scanner-orientations s2)]
|
||
|
(for [i (range 0 (count s1))]
|
||
|
(for [j (range 0 (count s2-or))]
|
||
|
(point-add (nth s1 i) (nth s2-or j))))))
|
||
|
|
||
|
(defn scanner-find-connection
|
||
|
"Attempt to determine if s1 and s2 are linked through common beacons."
|
||
|
[s1 s2]
|
||
3 years ago
|
(loop [potential-links (as-> (scanner-build-potential-links s1 s2) $
|
||
|
(for [i (range 0 (count $))] [i (nth $ i)])
|
||
|
(into [] $))]
|
||
|
(when-let [[orientation link] (first potential-links)]
|
||
|
(if-let [match (first (drop-while #(< (val %) 12) (frequencies (reduce concat link))))]
|
||
|
[orientation (key match)]
|
||
|
(recur (rest potential-links))))))
|
||
3 years ago
|
|
||
|
(defn scanner-merge
|
||
|
"Merges the report of linked scanner s2 into scanner s1."
|
||
|
[s1 s2 s2-or s2-coord]
|
||
3 years ago
|
(vec (into (set s1) (map #(point-add s2-coord (map - %)) (scanner-orientation s2 s2-or)))))
|
||
3 years ago
|
|
||
|
(let [[beacon-count scanner-coords]
|
||
3 years ago
|
(loop [new-reps (->> (slurp "./in") clojure.string/split-lines build-scanner-reports)
|
||
|
sc-coords '([0 0 0])
|
||
|
next-reps (rest new-reps)]
|
||
|
(if-let [next-rep (first next-reps)]
|
||
|
(if-let [[found-or found-coord] (scanner-find-connection (first new-reps) next-rep)]
|
||
|
(let [new-base (scanner-merge (first new-reps) next-rep found-or found-coord)
|
||
|
newest-reps (->> (assoc new-reps 0 new-base) (filterv (partial not= next-rep)))]
|
||
|
(println "found" found-coord)
|
||
|
(recur newest-reps (conj sc-coords found-coord) (rest newest-reps)))
|
||
|
(recur new-reps sc-coords (rest next-reps)))
|
||
|
[(count (first new-reps)) sc-coords]))]
|
||
3 years ago
|
(println "Part 1:" beacon-count)
|
||
|
(println "Part 2:"
|
||
|
(apply max
|
||
|
(for [p1 scanner-coords p2 scanner-coords :when (not= p1 p2)]
|
||
|
(apply + (map #(Math/abs %) (point-add (map - p1) p2)))))))
|
||
|
|