diff --git a/clojure/aoc.clj b/clojure/aoc.clj index 54c1455..4bf1531 100644 --- a/clojure/aoc.clj +++ b/clojure/aoc.clj @@ -2,44 +2,49 @@ (:require [clojure.string :as str])) -(defn parse-multiline-string - [s & [out-type sep]] - (->> (str/split s (or sep #"\n")) - ((case out-type - :int (partial map parse-long) - :list (partial map #(str/split % #"")) - :vector (partial mapv vec) - nil identity)))) - - -(defn read-input - [input & [out-type sep]] - (let [name (if (int? input) - (format "%02d" input) - input)] - (-> (str "inputs/" name ".txt") - slurp - (parse-multiline-string out-type sep)))) - - -(defn read-input-line - [input & [sep]] - (->> input - read-input - first - ((case sep - nil identity - (partial #(str/split % sep)))))) - -(defn read-input-paragraphs - [input & [out-type]] - (->> (read-input input nil #"\n\n") - (mapv #(parse-multiline-string % out-type)))) +(def empty-queue clojure.lang.PersistentQueue/EMPTY) +(defn read-file [file] + (let [name (if (int? file) + (format "%02d" file) + file)] + (slurp (str "inputs/" name ".txt")))) + + +(defn integers + [s & {:keys [negative?] + :or {negative? true}}] + (mapv parse-long + (re-seq (if negative? #"-?\d+" #"\d+") s))) + (defn string->digits [s] (->> (str/split s #"") - (mapv parse-long))) + (map parse-long) + (filterv some?))) + +(defn parse-input + [s & [parse-fn {:keys [word-sep nl-sep]}]] + (let [f (case parse-fn + :int parse-long + :ints integers + :digits string->digits + :chars vec + :words #(str/split % (or word-sep #" ")) + nil identity + parse-fn)] + (mapv f (str/split s (or nl-sep #"\n"))))) + +(defn parse-input-line + [input & [parse-fn word-sep]] + (-> input + (parse-input parse-fn {:word-sep word-sep}) + first)) + +(defn parse-input-paragraphs + [input & [parse-fn word-sep]] + (->> (parse-input input nil {:nl-sep #"\n\n"}) + (mapv #(parse-input % parse-fn {:word-sep word-sep})))) (defn transpose [matrix] @@ -57,17 +62,16 @@ (int (first s))) -(defn integers - [s & {:keys [negative?] - :or {negative? true}}] - (mapv parse-long - (re-seq (if negative? #"-?\d+" #"\d+") s))) - - (defn pt+ ^longs [[^long ax ^long ay] [^long bx ^long by]] [(+ ax bx) (+ ay by)]) -(defn neighbours ^longs [[^long x ^long y] ^long amount] +(defn inside? + ([size x y] (inside? size size x y)) + ([size-x size-y x y] + (and (< -1 x size-x) + (< -1 y size-y)))) + +(defn neighbours ^longs [^long amount [^long x ^long y]] (for [^long dy [-1 0 1] ^long dx [-1 0 1] :when @@ -84,8 +88,8 @@ [x y (dec z)] [x y (inc z)]]) -(defn vec2d->grid - ([v] (vec2d->grid v identity)) +(defn grid->points + ([v] (grid->points v identity)) ([v pred] (into {} (for [[y line] (map-indexed vector v) @@ -93,6 +97,7 @@ :when (pred char)] [[x y] char])))) + (defn none? [pred xs] ;; Faster version of `not-any?`. (reduce @@ -113,7 +118,7 @@ false (recur (dec idx) acc))))) -(defn count-if [pred xs] +(defn count-if ^long [pred xs] (reduce (fn [^long acc x] (if (pred x) (inc acc) acc)) @@ -126,3 +131,15 @@ (when (pred x) (reduced x))) nil xs)) + + +(defn gcd + ([] 1) + ([x] x) + ([a b] (if (zero? b) a + (recur b (mod a b))))) + +(defn lcm + ([] 1) + ([x] x) + ([a b] (/ (* a b) (gcd a b)))) diff --git a/clojure/day01.clj b/clojure/day01.clj index 4af546f..61a629a 100644 --- a/clojure/day01.clj +++ b/clojure/day01.clj @@ -3,12 +3,12 @@ (defn parse-input [input] - (->> (aoc/read-input-paragraphs input :int) + (->> (aoc/parse-input-paragraphs input :int) (map #(reduce + %)) (sort >))) (defn solve - ([] (solve 1)) + ([] (solve (aoc/read-file 1))) ([input] (let [calories (parse-input input)] [(first calories) diff --git a/clojure/day02.clj b/clojure/day02.clj index 01bb480..c4f0115 100644 --- a/clojure/day02.clj +++ b/clojure/day02.clj @@ -47,10 +47,9 @@ (transduce (map rules) + guide)) (defn solve - ([] (solve 2)) + ([] (solve (aoc/read-file 2))) ([input] - (let [strategy-guide (->> (aoc/read-input input) - (mapv #(take-nth 2 %)))] + (let [strategy-guide (aoc/parse-input input #(take-nth 2 %))] [(play p1-rules strategy-guide) (play p2-rules strategy-guide)]))) diff --git a/clojure/day03.clj b/clojure/day03.clj index 0a962e3..73d488a 100644 --- a/clojure/day03.clj +++ b/clojure/day03.clj @@ -29,9 +29,9 @@ (def f2 #(partition 3 %)) (defn solve - ([] (solve 3)) + ([] (solve (aoc/read-file 3))) ([input] - (let [rucksacks (aoc/read-input input)] + (let [rucksacks (aoc/parse-input input)] [(priority-sum rucksacks f1) (priority-sum rucksacks f2)]))) diff --git a/clojure/day04.clj b/clojure/day04.clj index 5931f5a..d035c65 100644 --- a/clojure/day04.clj +++ b/clojure/day04.clj @@ -11,11 +11,10 @@ (<= a c b))) (defn parse-input [input] - (->> (aoc/read-input input) - (mapv #(aoc/integers % {:negative? false})))) + (aoc/parse-input input #(aoc/integers % {:negative? false}))) (defn solve - ([] (solve 4)) + ([] (solve (aoc/read-file 4))) ([input] (let [assignments-pairs (parse-input input)] [(aoc/count-if fully-contain? assignments-pairs) diff --git a/clojure/day05.clj b/clojure/day05.clj index da55e85..1c6b84f 100644 --- a/clojure/day05.clj +++ b/clojure/day05.clj @@ -10,14 +10,14 @@ (remove empty?) (into [[]]))) ; empty first to have real stacks start from index 1 -(defn move-boxes [stacks [amount from to] & [pick-multiple?]] +(defn move-boxes [stacks [amount from to] pick-multiple?] (let [[took remains] (split-at amount (stacks from)) put (into (stacks to) (if pick-multiple? (reverse took) took))] (-> stacks (assoc! from remains) (assoc! to put)))) -(defn operate-crane [stacks instructions & pick-multiple?] +(defn operate-crane [stacks instructions pick-multiple?] (->> instructions (reduce #(move-boxes %1 %2 pick-multiple?) (transient stacks)) persistent! @@ -25,13 +25,13 @@ (apply str))) (defn solve - ([] (solve 5)) + ([] (solve (aoc/read-file 5))) ([input] - (let [[raw-stacks raw-instructions] (aoc/read-input-paragraphs input) + (let [[raw-stacks raw-instructions] (aoc/parse-input-paragraphs input) stacks (parse-stacks raw-stacks) instructions (mapv aoc/integers raw-instructions) - operate (partial operate-crane stacks instructions)] - [(operate) + operate #(operate-crane stacks instructions %)] + [(operate false) (operate :CrateMover9001)]))) diff --git a/clojure/day06.clj b/clojure/day06.clj index d6a306f..c53916e 100644 --- a/clojure/day06.clj +++ b/clojure/day06.clj @@ -14,9 +14,9 @@ (iterate inc length))) (defn solve - ([] (solve 6)) + ([] (solve (aoc/read-file 6))) ([input] - (let [datastream-buffer (aoc/read-input-line input)] + (let [datastream-buffer (aoc/parse-input-line input)] [(process datastream-buffer 4) (process datastream-buffer 14)]))) diff --git a/clojure/day07.clj b/clojure/day07.clj index b775c36..d414a4f 100644 --- a/clojure/day07.clj +++ b/clojure/day07.clj @@ -21,7 +21,7 @@ (:or ["$" "ls"] ["dir" _]) [sizes path] [size _] [(add! path size sizes) path])) - [(transient {}) []]) + [(transient {}) '()]) first persistent!)) @@ -38,9 +38,9 @@ (reduce min)))) (defn solve - ([] (solve 7)) + ([] (solve (aoc/read-file 7))) ([input] - (let [folders (calc-sizes (aoc/read-input input)) + (let [folders (calc-sizes (aoc/parse-input input)) total-size (folders ["/"]) sizes (vals folders)] [(part-1 sizes) diff --git a/clojure/day08.clj b/clojure/day08.clj index 8001b96..3907a02 100644 --- a/clojure/day08.clj +++ b/clojure/day08.clj @@ -34,11 +34,10 @@ :scenic-score (transduce (map #(viewing-distance height %)) * dirs)}))) (defn solve - ([] (solve 8)) + ([] (solve (aoc/read-file 8))) ([input] - (let [height-map (->> (aoc/read-input input) - (mapv aoc/string->digits)) - results (go-through-forrest height-map)] + (let [height-map (aoc/parse-input input :digits) + results (go-through-forrest height-map)] [(->> results (aoc/count-if :is-visible?)) (->> results (map :scenic-score) (reduce max))]))) diff --git a/clojure/day09.clj b/clojure/day09.clj index b09009a..4775a9b 100644 --- a/clojure/day09.clj +++ b/clojure/day09.clj @@ -59,9 +59,9 @@ motions)) (defn solve - ([] (solve 9)) + ([] (solve (aoc/read-file 9))) ([input] - (let [motions (mapcat parse-motion (aoc/read-input input)) + (let [motions (mapcat parse-motion (aoc/parse-input input)) [_ p1 p2] (simulate motions)] [(count p1) (count p2)]))) diff --git a/clojure/day10.clj b/clojure/day10.clj index 7e511b3..07bdd1f 100644 --- a/clojure/day10.clj +++ b/clojure/day10.clj @@ -27,15 +27,15 @@ (map str/join))) (defn solve - ([] (solve 10)) + ([] (solve (aoc/read-file 10))) ([input] (let [x-positions (->> input - aoc/read-input + aoc/parse-input read-instructions (reductions + 1))] ; x starts at 1 [(part-1 x-positions) (part-2 x-positions)]))) -(solve 10) +(solve) diff --git a/clojure/day11.clj b/clojure/day11.clj index 0111a4a..a699c28 100644 --- a/clojure/day11.clj +++ b/clojure/day11.clj @@ -65,11 +65,11 @@ (defn parse-input [input] (->> input - aoc/read-input-paragraphs + aoc/parse-input-paragraphs (mapv parse-monkey))) (defn solve - ([] (solve 11)) + ([] (solve (aoc/read-file 11))) ([input] (let [monkeys (parse-input input)] [(play-game monkeys 20 true) diff --git a/clojure/day12.clj b/clojure/day12.clj index cde952d..6048028 100644 --- a/clojure/day12.clj +++ b/clojure/day12.clj @@ -41,9 +41,9 @@ (- (int char) (int \a))]))) (defn solve - ([] (solve 12)) + ([] (solve (aoc/read-file 12))) ([input] - (let [grid (make-grid (aoc/read-input input :vector)) + (let [grid (make-grid (aoc/parse-input input :chars)) p1 (future (travel grid 1)) p2 (future (travel grid 2))] [@p1 @p2]))) diff --git a/clojure/day13.clj b/clojure/day13.clj index d6f178c..2fd7f2c 100644 --- a/clojure/day13.clj +++ b/clojure/day13.clj @@ -37,13 +37,13 @@ (defn parse-input [input] (->> input - aoc/read-input + aoc/parse-input (remove empty?) (mapv read-string) (partition 2))) (defn solve - ([] (solve 13)) + ([] (solve (aoc/read-file 13))) ([input] (let [packets (parse-input input)] [(part-1 packets) diff --git a/clojure/day14.clj b/clojure/day14.clj index 9a5dbf6..f5adbb0 100644 --- a/clojure/day14.clj +++ b/clojure/day14.clj @@ -52,12 +52,13 @@ (defn add-floor [rocks] (let [floor-y (+ 2 (reduce max (map #(mod % X) rocks))) - floor (for [x (range (- start-x floor-y) (+ start-x floor-y 1))] (+ (* X x) floor-y ))] + floor (for [x (range (- start-x floor-y) (+ start-x floor-y 1))] + (+ (* X x) floor-y))] (into rocks floor))) (defn parse-input [input] (->> input - aoc/read-input + aoc/parse-input (into (dense-int-set) (comp (map aoc/integers) @@ -67,7 +68,7 @@ add-floor)) (defn solve - ([] (solve 14)) + ([] (solve (aoc/read-file 14))) ([input] (let [rocks (parse-input input)] [(part-1 rocks) diff --git a/clojure/day15.clj b/clojure/day15.clj index 7c08835..38c876f 100644 --- a/clojure/day15.clj +++ b/clojure/day15.clj @@ -56,21 +56,16 @@ :when (and (< 0 x limit) (< 0 y limit))] [x y])] (->> potential-locations - (aoc/find-first (partial found-beacon? sensors)) + (aoc/find-first #(found-beacon? sensors %)) calc-score))) (defn solve - ([] (solve 15)) - ([input & [limit]] - (let [limit (if (= (str input) "15") - 4000000 - (or limit 20)) - sensors (->> input - aoc/read-input - (map aoc/integers) - (map find-radius))] + ([] (solve (aoc/read-file 15))) + ([input] + (let [limit 4000000 + sensors (aoc/parse-input input (comp find-radius aoc/integers))] [(part-1 sensors (/ limit 2)) (part-2 sensors limit)]))) diff --git a/clojure/day16.clj b/clojure/day16.clj index 36deab8..0204aa0 100644 --- a/clojure/day16.clj +++ b/clojure/day16.clj @@ -86,7 +86,7 @@ (defn part-1 [[valves conns flows]] (->> (traverse 0 valves conns {} 30) (transduce - (map (partial score flows)) + (map #(score flows %)) max 0))) (defn part-2 [[valves conns flows]] @@ -98,17 +98,15 @@ (reduce max))) (defn parse-input [input] - (let [[all-flows direct-conns] - (->> (aoc/read-input input) - (mapv parse-line) - create-connections) + (let [[all-flows direct-conns] (create-connections + (aoc/parse-input input parse-line)) conns (floyd-warshall (keys all-flows) direct-conns) flows (into {} (filter #(pos? (val %))) all-flows) valves (set (keys flows))] [valves conns flows])) (defn solve - ([] (solve 16)) + ([] (solve (aoc/read-file 16))) ([input] (let [data (parse-input input) p1 (future (part-1 data)) diff --git a/clojure/day17.clj b/clojure/day17.clj index 6345cdb..8204b97 100644 --- a/clojure/day17.clj +++ b/clojure/day17.clj @@ -5,8 +5,8 @@ (defrecord Point [x y]) -(defmacro ->Rock [pts] - `(map (fn [[x# y#]] (->Point x# y#)) ~pts)) +(defn ->Rock [pts] + (map (fn [[x y]] (->Point x y)) pts)) (def rocks [(->Rock [[0 0] [1 0] [2 0] [3 0]]) @@ -32,7 +32,7 @@ (defn peaks-hash [tower max-y] (transduce (map (fn [{:keys [x y]}] - (if (= y max-y) (* x x) 0))) + (if (= y max-y) (* x x) 0))) + tower)) @@ -45,7 +45,7 @@ [0 0 0 0 (transient {}) tower]] (if (>= r rounds) (+ max-y skipped) (let [init-pos (->Point 2 (+ max-y 4)) - rock (mapv (partial pt+ init-pos) (nth rocks (mod r R)))] + rock (mapv #(pt+ init-pos %) (nth rocks (mod r R)))] (recur (loop [rock rock m (long m)] @@ -77,9 +77,9 @@ (defn solve - ([] (solve 17)) + ([] (solve (aoc/read-file 17))) ([input] - (let [movements (aoc/read-input-line input)] + (let [movements (aoc/parse-input-line input)] [(play movements 2022) (play movements 1000000000000)]))) diff --git a/clojure/day18.clj b/clojure/day18.clj index 2d7dc28..727d5dd 100644 --- a/clojure/day18.clj +++ b/clojure/day18.clj @@ -24,12 +24,12 @@ (defn parse-input [filename] (->> filename - aoc/read-input + aoc/parse-input (map aoc/integers) set)) (defn solve - ([] (solve 18)) + ([] (solve (aoc/read-file 18))) ([input] (let [cubes (parse-input input) neighbours (mapcat aoc/neighbours-3d cubes) diff --git a/clojure/day19.clj b/clojure/day19.clj index d4ac264..ace2991 100644 --- a/clojure/day19.clj +++ b/clojure/day19.clj @@ -112,29 +112,23 @@ (defn part-1 [blueprints] (->> blueprints - (pmap (partial geodes 24)) + (pmap #(geodes 24 %)) (mapv * (iterate inc 1)) (reduce +))) (defn part-2 [blueprints] (->> blueprints (take 3) - (pmap (partial geodes 32)) + (pmap #(geodes 32 %)) (reduce *))) -(defn parse-input [input] - (->> input - aoc/read-input - (mapv aoc/integers) - (mapv line->blueprint))) - (defn solve - ([] (solve 19)) + ([] (solve (aoc/read-file 19))) ([input] - (let [blueprints (parse-input input) + (let [blueprints (aoc/parse-input input (comp line->blueprint aoc/integers)) p1 (future (part-1 blueprints)) p2 (future (part-2 blueprints))] [@p1 @p2]))) -(solve) +(time (solve)) diff --git a/clojure/day20.clj b/clojure/day20.clj index f520265..07bdaaf 100644 --- a/clojure/day20.clj +++ b/clojure/day20.clj @@ -66,11 +66,11 @@ (reduce +)))) (defn solve - ([] (solve 20)) + ([] (solve (aoc/read-file 20))) ([input] - (let [nums (aoc/read-input input :int) + (let [nums (aoc/parse-input input :int) links1 (nums->links nums) - links2 (nums->links (map (partial * 811589153) nums)) + links2 (nums->links (map #(* % 811589153) nums)) p1 (future (mix links1 1)) p2 (future (mix links2 10))] [@p1 @p2]))) diff --git a/clojure/day21.clj b/clojure/day21.clj index 8616646..99b66dd 100644 --- a/clojure/day21.clj +++ b/clojure/day21.clj @@ -6,7 +6,7 @@ (defn parse-line [line] (let [[mkey & ops] (str/split line #" ") - monkey (keyword (apply str (drop-last mkey))) + monkey (keyword (str/join (drop-last mkey))) yell (if (= (count ops) 1) {:val (parse-long (first ops))} (let [[l op r] ops] @@ -17,7 +17,7 @@ (defn dfs [monkeys m] (let [{:keys [val op left right]} (monkeys m)] - (if val val + (or val (op (dfs monkeys left) (dfs monkeys right))))) (defn human-yell [monkeys] @@ -34,10 +34,9 @@ (recur lo mid))))))) (defn solve - ([] (solve 21)) + ([] (solve (aoc/read-file 21))) ([input] - (let [monkeys (->> (aoc/read-input input) - (into {} (map parse-line)))] + (let [monkeys (into {} (aoc/parse-input input parse-line))] [(dfs monkeys :root) (human-yell monkeys)]))) diff --git a/clojure/day22.clj b/clojure/day22.clj index ba678c1..ca6f726 100644 --- a/clojure/day22.clj +++ b/clojure/day22.clj @@ -36,7 +36,7 @@ (reduce (fn [[p d] _] (let [p' (aoc/pt+ p d) - [p'' d'] (if (not (contains? grid p')) + [p'' d'] (if-not (contains? grid p') (wrap-fn p' d) [p' d])] (if (= \. (grid p'' \#)) @@ -63,14 +63,14 @@ ({[1 0] 0 , [0 1] 1 , [-1 0] 2 , [0 -1] 3} d))) (defn solve - ([] (solve 22)) + ([] (solve (aoc/read-file 22))) ([input] - (let [[field [moves]] (aoc/read-input-paragraphs input) + (let [[field [moves]] (aoc/parse-input-paragraphs input) start [(str/index-of (first field) \.) 0] moves (-> moves (str/replace #"(L|R)" " $1 ") (str/split #" ")) - grid (aoc/vec2d->grid field #{\. \#}) + grid (aoc/grid->points field #{\. \#}) move_ (partial traverse start moves grid)] [(-> (move_ wrap-1) password) (-> (move_ wrap-2) password)]))) diff --git a/clojure/day23.clj b/clojure/day23.clj index aa68ee8..70eaee3 100644 --- a/clojure/day23.clj +++ b/clojure/day23.clj @@ -85,7 +85,7 @@ (defn parse-input [input] (->> input - aoc/read-input + aoc/parse-input (#(for [[y line] (map-indexed vector %) [x char] (map-indexed vector line) :when (= char \#)] @@ -94,7 +94,7 @@ dense-int-set)) (defn solve - ([] (solve 23)) + ([] (solve (aoc/read-file 23))) ([input] (let [elves (parse-input input)] [(part-1 elves) diff --git a/clojure/day24.clj b/clojure/day24.clj index 6414398..f2429ff 100644 --- a/clojure/day24.clj +++ b/clojure/day24.clj @@ -33,7 +33,7 @@ (defn parse-input [input] - (let [inp (aoc/read-input input :vector) + (let [inp (aoc/parse-input input :chars) res (for [[y line] (map-indexed vector inp) [x char] (map-indexed vector line) :let [pt [(dec x) (dec y)] @@ -54,7 +54,7 @@ [blizzards walls w h])) (defn solve - ([] (solve 24)) + ([] (solve (aoc/read-file 24))) ([input] (let [[_ _ w h :as data] (parse-input input) start -N @@ -69,4 +69,3 @@ (solve) - diff --git a/clojure/day25.clj b/clojure/day25.clj index e2705a5..955126c 100644 --- a/clojure/day25.clj +++ b/clojure/day25.clj @@ -17,16 +17,16 @@ (defn to-5 [number] (loop [d number acc '()] - (if (zero? d) (apply str acc) + (if (zero? d) (str/join acc) (let [m (mod (+ d 2) 5) d (quot (+ d 2) 5) digit (nth digits m)] (recur d (cons digit acc)))))) (defn solve - ([] (solve 25)) + ([] (solve (aoc/read-file 25))) ([input] - (let [numbers (aoc/read-input input)] + (let [numbers (aoc/parse-input input)] (->> numbers (map from-5) (reduce +) diff --git a/clojure/tests/solutions_tests.clj b/clojure/tests/solutions_tests.clj index 94cd266..5328452 100644 --- a/clojure/tests/solutions_tests.clj +++ b/clojure/tests/solutions_tests.clj @@ -4,7 +4,7 @@ day11 day12 day13 day14 day15 day16 day17 day18 day19 day20 day21 day22 day23 day24 day25 - [clojure.test :refer [deftest are run-tests successful?]])) + [clojure.test :refer [deftest is run-tests successful?]])) (defmacro check-day [day test-results real-results] @@ -15,9 +15,11 @@ test-input (str day "_test") real-input day] `(deftest ~test-name - (are [input output] (= (~solve-fn input) output) - ~test-input ~test-results - ~real-input ~real-results)))) + (when ~test-results + (is (= ~test-results (~solve-fn (aoc/read-file ~test-input))))) + (is (= ~real-results (~solve-fn (aoc/read-file ~real-input))))))) + + (def test-output-10 @@ -51,14 +53,14 @@ (check-day 12 [31 29] [352 345]) (check-day 13 [13 140] [5013 25038]) (check-day 14 [24 93] [1003 25771]) -(check-day 15 [26 56000011] [5299855 13615843289729]) +(check-day 15 nil [5299855 13615843289729]) (check-day 16 [1651 1707] [1880 2520]) (check-day 17 [3068 1514285714288] [3232 1585632183915]) (check-day 18 [64 58] [3326 1996]) (check-day 19 [33 3472] [994 15960]) (check-day 20 [3 1623178306] [5904 8332585833851]) (check-day 21 [152 301] [63119856257960 3006709232464]) -(check-day 22 [8048 8048] [13566 11451]) ; hardcoded for real input --> wrong test results +(check-day 22 nil [13566 11451]) (check-day 23 [110 20] [3762 997]) (check-day 24 [18 54] [266 853]) (check-day 25 "2=-1=0" "122-2=200-0111--=200")