From bef47f68b7ebb1401d8ebb6532edd4cf7d6645c4 Mon Sep 17 00:00:00 2001 From: Adam Jeniski Date: Wed, 25 Dec 2024 12:10:32 -0500 Subject: [PATCH] WIP; init shared clj lib --- shared/clj/project.clj | 8 ++ shared/clj/src/core.clj | 172 +++++++++++++++++++++++++++++++ shared/clj/src/input_manager.clj | 28 +++++ shared/clj/test/core_test.clj | 24 +++++ 4 files changed, 232 insertions(+) create mode 100644 shared/clj/project.clj create mode 100644 shared/clj/src/core.clj create mode 100644 shared/clj/src/input_manager.clj create mode 100644 shared/clj/test/core_test.clj diff --git a/shared/clj/project.clj b/shared/clj/project.clj new file mode 100644 index 0000000..5257360 --- /dev/null +++ b/shared/clj/project.clj @@ -0,0 +1,8 @@ +(defproject org.ajet/advent-of-code "0.0.0-SNAPSHOT" + :description "my 2024 advent of code solutions in clojure" + :url "http://github.com/ajetski/advent-of-code" + :min-lein-version "2.0.0" + :dependencies [[org.clojure/clojure "1.12.0"] + [org.clojure/math.combinatorics "0.3.0"] + [babashka/fs "0.5.23"] + [org.babashka/http-client "0.4.22"]]) diff --git a/shared/clj/src/core.clj b/shared/clj/src/core.clj new file mode 100644 index 0000000..3711cd1 --- /dev/null +++ b/shared/clj/src/core.clj @@ -0,0 +1,172 @@ +(ns core + (:require + [clojure.string :as str])) + +;; string/regex stuff + +(defn split-on-comma [s] + (str/split s #",")) + +(defn split-whitespace [s] + (str/split s #"\s+")) + +(defn split-on-double-newlines [s] + (str/split s #"\n\n")) + +(defn get-match-groups [regex s] + (->> s (re-seq regex) (map rest))) + +(defn re-pos [re s] + (loop [m (re-matcher re s) + res {}] + (if (.find m) + (recur m (assoc res (.start m) (.group m))) + res))) + + +;; general utils + +(defn dbg + "prints a value and returns it. + useful for inserting into ->, ->>, and comp + + (->> x + some-fn + dbg + some-fn-2)" + [x] + (println x) + x) + +(defn log + "faster than dbg, for those really tricky graph problems + or just ya know, writing to your diary" + ([msg] + (spit "logs.txt" msg :append true)) + ([file msg] + (spit file msg :append true))) + +(defn compose + "just comp, but in the \"right\" order (left-to-right evaluation of fns)" + ;; several inlined arrities for optimization + ([f1] + f1) + ([f1 f2] + (comp f2 f1)) + ([f1 f2 f3] + (comp f3 f2 f1)) + ([f1 f2 f3 f4 & fs] + (comp (apply comp (reverse fs)) f4 f3 f2 f1))) + + +;; alter collections + +(defn get-coords [list-of-lists] + (for [row (range (count list-of-lists)) + col (range (count (get list-of-lists row)))] + [row col])) + +(defn map-by-coords [arr-2d] + (->> arr-2d + get-coords + (map (juxt identity #(get (get arr-2d (first %)) (second %)))) + (into {}))) + +(defn insert-at-idx [coll idx el] + (concat (take idx coll) + (list el) + (drop idx coll))) + +(defn mmap + "map map f coll" + [f & colls] + (apply map (partial map f) colls)) + +(defn mmapv + "mapv mapv f coll" + [f & colls] + (apply mapv (partial mapv f) colls)) + +(defn mmmap + "map map map f coll" + [f & colls] + (apply map (partial map (partial map f)) colls)) + +(defn mmmapv + "mapv mapv mapv f coll" + [f & colls] + (apply mapv (partial mapv (partial mapv f)) colls)) + +(defn partition-by-counts [counts coll] + (->> counts + (reduce (fn [[acc coll] c] + (let [[a b] (split-at c coll)] + [(conj acc a) b])) + [[] coll]) + first)) + +(defn update-last [v f & args] + (let [idx (dec (count v))] + (apply update v idx f args))) + +(defn partition-contiguous-nums [sorted-nums] + (:acc (reduce (fn [{acc :acc + a :last} + el] + (if (= el (inc a)) + {:acc (update-last acc conj el) + :last el} + {:acc (conj acc [el]) + :last el})) + {:acc [[(first sorted-nums)]] + :last (first sorted-nums)} + (rest sorted-nums)))) + + +;; Math things + +(defn square [n] (* n n)) +(defn mean [a] (/ (reduce + a) (count a))) +(defn standard-deviation [a] + (let [mn (mean a)] + (Math/sqrt + (/ (reduce #(+ %1 (square (- %2 mn))) 0 a) + (dec (count a)))))) + +(defn cartesian-product [a b] + (partition (count b) + (for [el1 a + el2 b] + (* el1 el2)))) + + +;; conversions + +(def arrow-char->dir {\> :right + \v :down + \< :left + \^ :up}) + +(defn bool->binary-int + "converts bool to integral binary representation (0 or 1)" + [condition] + (if condition 1 0)) + + +;; 👻 macros 👻 + +(defmacro pfor + "pairwise for, like clojure.core/for except NOT cartesian. + linear result over arrs" + [bindings & body] + (let [vars (take-nth 2 bindings) + forms (take-nth 2 (rest bindings))] + `(map (fn [~@vars] ~@body) ~@forms))) + +(comment + (def ^:private a [1 2 3]) + (def ^:private b [3 2 1]) + (pfor [x a + y b + z [0 1 2]] + (+ x y z))) diff --git a/shared/clj/src/input_manager.clj b/shared/clj/src/input_manager.clj new file mode 100644 index 0000000..1027915 --- /dev/null +++ b/shared/clj/src/input_manager.clj @@ -0,0 +1,28 @@ +(ns input-manager + (:require + [babashka.fs :as fs] + [babashka.http-client :as http] + [clojure.string :as str])) + +(def session (delay (first (fs/read-all-lines "input/.session")))) + +(defn input-file-loc [year day] + (str "input/" year "-" day ".txt")) + +(defn download-input [year day] + (->> {:headers {:cookie (str "session=" @session)}} + (http/get (str "https://adventofcode.com/" year "/day/" day "/input")) + :body + (str/split-lines) + (fs/write-lines (input-file-loc year day)))) + +(defn get-input [year day] + (try + (fs/read-all-lines (input-file-loc year day)) + (catch java.nio.file.NoSuchFileException _e + (download-input year day) + (get-input year day)))) + +(defn get-input-raw [year day] + (str/join "\n" (get-input year day))) + diff --git a/shared/clj/test/core_test.clj b/shared/clj/test/core_test.clj new file mode 100644 index 0000000..c622834 --- /dev/null +++ b/shared/clj/test/core_test.clj @@ -0,0 +1,24 @@ +(ns core-test + (:require [core :as c] + [clojure.test :refer :all :as t])) + +(comment + (use 'clojure.repl) + + #_{:clj-kondo/ignore [:unresolved-symbol]} + (dir c) + + (run-tests) + + ; + ) + +(deftest math-test + (testing "math utils" + (is (= 1 1)))) + +(deftest utils-test + (testing "general utils")) + +(deftest coll-lib-test + (testing "alter coll utils"))