Last active
December 22, 2021 02:32
-
-
Save borkdude/b5cf0e9d2d8ab7c678d88e27a3357b33 to your computer and use it in GitHub Desktop.
Advent of Code 2021 - in Clojure / babashka 0.6.8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-01 | |
(:require [clojure.string :as str])) | |
(def input (map parse-long (str/split-lines (slurp "input.txt")))) | |
(defn answer-01 [input] | |
(count (filter true? (map < input (rest input))))) | |
(def three-sliding-window | |
(map + input (next input) (nnext input))) | |
(defn answer-02 [] | |
(answer-01 three-sliding-window)) | |
(prn (answer-01 input)) | |
(prn (answer-02)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-01 | |
(:require [clojure.edn :as edn] | |
[clojure.string :as str])) | |
(def input (map (fn [line] | |
(edn/read-string (format "[%s]" line))) | |
(str/split-lines (slurp "input_d02.txt")))) | |
(defn answer-01 [] | |
(let [state {:horizontal 0 :depth 0}] | |
(->> (reduce (fn [state [sym n]] | |
(case sym | |
forward (update state :horizontal + n) | |
up (update state :depth - n) | |
down (update state :depth + n))) | |
state | |
input) | |
((juxt :horizontal :depth)) | |
(apply *)))) | |
(prn (answer-01)) | |
(defn answer-02 [] | |
(let [state {:horizontal 0 :depth 0 :aim 0}] | |
(->> (reduce (fn [state [sym n]] | |
(case sym | |
forward (-> state | |
(update :horizontal + n) | |
(update :depth + (* n (:aim state)))) | |
up (update state :aim - n) | |
down (update state :aim + n))) | |
state | |
input) | |
((juxt :horizontal :depth)) | |
(apply *)))) | |
(prn (answer-02)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-01 | |
(:require [clojure.string :as str])) | |
(def raw-input (str/split-lines #_sample (slurp "input_d03.txt"))) | |
(def input | |
(map #(Integer/parseInt % 2) raw-input)) | |
(defn mask-fn [mask num] | |
(= mask (bit-and mask num))) | |
(defn mask-filter [mask input] | |
(let [most-ones (filter (partial mask-fn mask) input) | |
most-zeroes (remove (partial mask-fn mask) input)] | |
[most-ones most-zeroes])) | |
(def len (count (first raw-input))) | |
(def powers (reverse (take len (iterate #(* 2 %) 1)))) | |
(defn answer-01 [] | |
(let [gamma | |
(reduce (fn [bits mask] | |
(let [[most-ones most-zeroes] (mask-filter mask input) | |
more-ones? (>= (count most-ones) (count most-zeroes))] | |
(if more-ones? | |
(conj bits 1) (conj bits 0)))) | |
[] | |
powers) | |
gamma (Integer/parseInt (apply str gamma) 2) | |
epsilon (bit-xor (apply + powers) gamma)] | |
(* gamma epsilon))) | |
(prn (answer-01)) | |
(defn answer-02 [] | |
(let [[[oxygen] [co2]] | |
(reduce (fn [[pt1 pt2] mask] | |
(let [[_ new-pt1] (sort-by count (mask-filter mask pt1)) | |
[new-pt2 _] (sort-by count (mask-filter mask pt2))] | |
[(or (seq new-pt1) | |
pt1) | |
(or (seq new-pt2) | |
pt2)])) | |
[input input] | |
powers)] | |
(* oxygen co2))) | |
(answer-02) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-03 | |
(:require [clojure.set :as set] | |
[clojure.string :as str])) | |
(def raw-input (->> (str/split-lines (slurp "input_d04.txt")) | |
(partition-by str/blank?))) | |
(def numbers (map parse-long (str/split (ffirst raw-input) #","))) | |
(def raw-boards (->> raw-input nnext (remove #(= '("") %)))) | |
(def boards (map (fn [strs] | |
(mapv #(mapv parse-long (str/split (str/trim %) #"\s+")) strs)) | |
raw-boards)) | |
(defn valid-board* [board nums] | |
(some #(when (empty? (set/difference (set %) (set nums))) | |
board) | |
board)) | |
(defn valid-board [board nums] | |
(when (or (valid-board* board nums) | |
(valid-board* (apply map vector board) nums)) | |
board)) | |
(defn answer-01 [] | |
(let [board (reduce | |
(fn [nums num] | |
(let [nums (conj nums num)] | |
(if-let [board (some #(valid-board % nums) boards)] | |
(let [board (reduce into #{} board) | |
nums (set/difference board nums) | |
sum (apply + nums)] | |
(reduced (* num sum))) | |
nums))) | |
#{} | |
numbers)] | |
board)) | |
(time (prn (answer-01))) | |
(defn answer-02 [] | |
(let [[_ nums boards] | |
(-> (reduce | |
(fn [[boards nums winning-boards] num] | |
(if (empty? boards) | |
(reduced [nil nums winning-boards]) | |
(let [nums (conj nums num) | |
matching-boards (keep #(valid-board % nums) boards) | |
remaining-boards (remove | |
(fn [board] | |
(some #(= board %) matching-boards)) | |
boards)] | |
[remaining-boards nums (into winning-boards matching-boards)]))) | |
[boards [] []] | |
numbers)) | |
board (last boards) | |
board (reduce into #{} board) | |
non-matching-nums (set/difference board (set nums)) | |
sum (apply + non-matching-nums)] | |
(* (last nums) sum))) | |
(time (prn (answer-02))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-05 | |
(:require [clojure.string :as str])) | |
(defn coordinate [s] | |
(mapv parse-long (str/split s #","))) | |
(def input (->> (str/split-lines (slurp "input_d05.txt")) | |
(map #(mapv coordinate (str/split % #" -> "))))) | |
(defn rng [x y] | |
(if (< x y) | |
(range x (inc y)) | |
(range x (dec y) -1))) | |
(defn line | |
[[[x1 y1] [x2 y2]] diagonal?] | |
(cond (= x1 x2) | |
(mapv vector (repeat x1) (rng y1 y2)) | |
(= y1 y2) | |
(mapv vector (rng x1 x2) (repeat y1)) | |
:else (when diagonal? | |
(mapv vector (rng x1 x2) (rng y1 y2))))) | |
(defn answer [diagonal?] | |
(->> (frequencies (mapcat #(line % diagonal?) input)) | |
(filter (fn [[_ v]] | |
(> v 1))) | |
(count))) | |
(defn answer-01 [] | |
(answer false)) | |
(prn (answer-01)) | |
(defn answer-02 [] | |
(answer true)) | |
(prn (answer-02)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-06 | |
(:require [clojure.string :as str])) | |
(def input (map parse-long (str/split (str/trim (slurp "input_d06.txt")) #","))) | |
(def initial (reduce | |
(fn [vec [k v]] | |
(assoc vec k v)) | |
(vec (repeat 9 0)) | |
(frequencies input))) | |
(defn update-state [[zeroes & tail]] | |
(-> (vec tail) | |
(conj zeroes) | |
(update 6 + zeroes))) | |
(def days (iterate update-state initial)) | |
(defn answer-01 [] | |
(apply + (nth days 80))) | |
(time (prn (answer-01))) | |
(defn answer-02 [] | |
(apply + (nth days 256))) | |
(time (prn (answer-02))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-07 | |
(:require [clojure.string :as str])) | |
(def input (map parse-long | |
(str/split (str/trim (slurp "input_d07.txt")) #","))) | |
(defmacro when-not-bb [& body] | |
(when-not (System/getProperty "babashka.version") | |
`(do ~@body))) | |
(when-not-bb (set! *unchecked-math* :warn-on-boxed)) | |
(defn dist [^long x ^long y] | |
(if (> x y) | |
(- x y) (- y x))) | |
(def freqs (frequencies input)) | |
(defn answer [cost-fn] | |
(reduce | |
(fn [^long min-cost ^long pos] | |
(let [sum (reduce (fn [^long cost [crab-pos ^long freq]] | |
(let [distance (dist pos crab-pos) | |
sum (+ cost (* freq ^long (cost-fn distance)))] | |
(if (> sum min-cost) | |
(reduced min-cost) | |
sum))) | |
0 | |
freqs)] | |
sum)) | |
Integer/MAX_VALUE | |
(range (apply min input) (apply max input)))) | |
(defn answer-01 [] | |
(answer identity)) | |
(time (prn (answer-01))) | |
(defn stepwise-sum [^long n] | |
(/ (* n (inc n)) 2)) | |
(time (prn (answer stepwise-sum))) | |
;; $ clojure -M aoc21_07.clj | |
;; 348996 | |
;; "Elapsed time: 130.094393 msecs" | |
;; 98231647 | |
;; "Elapsed time: 99.002943 msecs" | |
;; $ bb aoc21_07.clj | |
;; 348996 | |
;; "Elapsed time: 2200.714486 msecs" | |
;; 98231647 | |
;; "Elapsed time: 1896.988328 msecs" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-09 | |
(:require [clojure.set :as set] | |
[clojure.string :as str])) | |
(def input (mapv (fn [line] | |
(mapv #(parse-long (str %)) line)) | |
(str/split-lines #_sample (slurp "input_d09.txt")))) | |
(def num-cols (count (first input))) | |
(def num-rows (count input)) | |
(defn adjacent-coordinates [x y] | |
[[(dec x) y] [(inc x) y] [x (inc y)] [x (dec y)]]) | |
(defn get-height [[x y]] | |
(get-in input [y x] nil)) | |
(defn adjacent-heights [[x y]] | |
(keep get-height (adjacent-coordinates x y))) | |
(defn low-point [coord] | |
(let [height (get-height coord) | |
surrounding (adjacent-heights coord)] | |
(< height (apply min surrounding)))) | |
(def all-coordinates (for [x (range 0 num-cols) | |
y (range 0 num-rows)] | |
[x y])) | |
(defn low-points [] | |
(let [coords all-coordinates] | |
(filter low-point coords))) | |
(defn risk [coord] | |
(inc (get-height coord))) | |
(defn answer-01 [] | |
(apply + (map risk (low-points)))) | |
(time (prn (answer-01))) | |
(defn neighbours [[x y]] | |
(filter #(let [height (get-height %)] | |
(and height (not= 9 height))) | |
(adjacent-coordinates x y))) | |
(defn basin [low-point] | |
(loop [basin #{low-point}] | |
(let [expanded (set/difference (set (mapcat neighbours basin)) basin)] | |
(if (empty? expanded) | |
basin | |
(recur (into basin expanded)))))) | |
(defn answer-02 [] | |
(->> (sort-by count > (map basin (low-points))) | |
(take 3) | |
(map count) | |
(apply *))) | |
(time (prn (answer-02))) | |
;; $ time bb aoc21_d09.clj | |
;; 633 | |
;; "Elapsed time: 165.947235 msecs" | |
;; 1050192 | |
;; "Elapsed time: 406.951735 msecs" | |
;; bb aoc21_d09.clj 0.52s user 0.10s system 99% cpu 0.621 total | |
;; $ time clojure -M aoc21_d09.clj | |
;; 633 | |
;; "Elapsed time: 55.004862 msecs" | |
;; 1050192 | |
;; "Elapsed time: 196.477371 msecs" | |
;; clojure -M aoc21_d09.clj 2.54s user 0.22s system 204% cpu 1.353 total |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns aoc21-10 | |
(:require [clojure.string :as str])) | |
(def input (str/split-lines (slurp "input_d10.txt"))) | |
(def delims "()[]{}<>") | |
(def opening (take-nth 2 delims)) | |
(def closing (take-nth 2 (rest delims))) | |
(def opposite (zipmap opening closing)) | |
(def score (zipmap closing [3 57 1197 25137])) | |
(defn parse-line [input] | |
(reduce (fn [acc ch] | |
(if-let [closing (opposite ch)] | |
(update acc :closing conj closing) | |
(let [fst (first (:closing acc))] | |
(if-not (= fst ch) | |
(reduced {:incorrect ch}) | |
(update acc :closing rest))))) | |
{:closing '()} | |
input)) | |
(def parsed (mapv parse-line input)) | |
(defn answer-01 [] | |
(apply + (map score (keep :incorrect parsed)))) | |
(time (prn (answer-01))) | |
(def autocomplete-scores (zipmap closing [1 2 3 4])) | |
(defn autocomplete-score [chars] | |
(reduce (fn [sum ac-score] | |
(+ (* 5 sum) ac-score)) | |
0 | |
(map autocomplete-scores chars ))) | |
(defn answer-02 [] | |
(-> (map autocomplete-score (keep :closing parsed)) | |
sort | |
(as-> $ (nth $ (/ (count $) 2))))) | |
(time (prn (answer-02))) | |
;; $ bb aoc21_d10.clj | |
;; 344193 | |
;; "Elapsed time: 0.764751 msecs" | |
;; 3241238967 | |
;; "Elapsed time: 1.308886 msecs" | |
;; $ clojure -M aoc21_d10.clj | |
;; 344193 | |
;; "Elapsed time: 3.658084 msecs" | |
;; 3241238967 | |
;; "Elapsed time: 3.859535 msecs" |
It was somewhat intentional to not have parse-long do everything, but will consider.
@puredanger If it helps for the portable clojure.math
, JS supports a similar API:
(js/parseInt "100" 2)
4
Surprisingly the code for day 4 runs somewhat faster in babashka than clojure:
$ bb aoc21_04.clj
32844
122.147231 msecs
4920
451.069084 msecs
$ clojure -M aoc21_04.clj
32844
180.087077 msecs
4920
521.740564 msecs
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@puredanger Unfortunately
parse-long
doesn't support the radix argument, so I had to still use#(Integer/parseInt % 2)
in day 3.