Skip to content

Instantly share code, notes, and snippets.

@borkdude
Last active December 22, 2021 02:32
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borkdude/b5cf0e9d2d8ab7c678d88e27a3357b33 to your computer and use it in GitHub Desktop.
Save borkdude/b5cf0e9d2d8ab7c678d88e27a3357b33 to your computer and use it in GitHub Desktop.
Advent of Code 2021 - in Clojure / babashka 0.6.8
(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))
(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))
(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)
(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)))
(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))
(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)))
(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"
(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
(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"
@pradeepbishnoi
Copy link

@puredanger
Copy link

I'll be watching these for prevalence of 1.11 cleanup opportunities like getting rid of #(Integer/parseInt %) !

@theronic
Copy link

theronic commented Dec 2, 2021

@borkdude TIL you don't have to quote case symbols, e.g. (case x forward ...).

@borkdude
Copy link
Author

borkdude commented Dec 2, 2021

@puredanger parse-long etc. are now available in babashka 0.6.8 as well, updated day 1 :)

@borkdude
Copy link
Author

borkdude commented Dec 3, 2021

@puredanger Unfortunately parse-long doesn't support the radix argument, so I had to still use #(Integer/parseInt % 2) in day 3.

@puredanger
Copy link

It was somewhat intentional to not have parse-long do everything, but will consider.

@borkdude
Copy link
Author

borkdude commented Dec 3, 2021

@puredanger If it helps for the portable clojure.math, JS supports a similar API:

(js/parseInt "100" 2)
4

@borkdude
Copy link
Author

borkdude commented Dec 4, 2021

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