diff --git a/2025/project.clj b/2025/project.clj index 2f76275..e46d80e 100644 --- a/2025/project.clj +++ b/2025/project.clj @@ -9,5 +9,6 @@ [org.clojure/math.combinatorics "0.3.0"] [babashka/fs "0.5.23"] [org.babashka/http-client "0.4.22"] - [org.clojure/core.match "1.1.0"]] + [org.clojure/core.match "1.1.0"] + [clj-python/libpython-clj "2.026"]] :source-paths ["src" "../shared/clj/src"]) diff --git a/2025/src/day10.clj b/2025/src/day10.clj new file mode 100644 index 0000000..567adc6 --- /dev/null +++ b/2025/src/day10.clj @@ -0,0 +1,94 @@ +(ns day10 + (:require input-manager + [core :refer [dbg]] + [clojure.math.combinatorics :as combo] + [clojure.string :as str] + [libpython-clj2.require :refer [require-python]] + [libpython-clj2.python :as py])) + +(defn parse-line [line] + {:indicators (->> line + (drop 1) + (take-while (complement #{\]})) + (mapv (partial = \#))) + :buttons (->> line + (re-seq #"\([\d,]+\)") + (map #(map parse-long (re-seq #"\d+" %)))) + :joltages (->> line + (drop-while #(not= % \{)) + (drop 1) + (take-while #(not= % \})) + (apply str) + (re-seq #"\d+") + (mapv parse-long))}) + +(def input (map parse-line (input-manager/get-input 2025 10))) + +(defn apply-ops [v ops] + (reduce (fn [acc op] + (reduce #(update %1 %2 not) acc op)) + v + ops)) + +(defn minimum-button-mashing [{ops :buttons v :indicators}] + (loop [i 0 + cnt 1] + (if (>= i (combo/count-combinations ops cnt)) + (recur 0 (inc cnt)) + (let [v' (apply-ops v (combo/nth-combination ops cnt i))] + (if (every? (complement identity) v') + cnt + (recur (inc i) cnt)))))) + +;; part 1 +(apply + (map minimum-button-mashing input)) + +;; clj-python setup +(defmacro ! [self method & args] `(py/call-attr ~method ~self ~@args)) +(defmacro py= [self & args] `(! "__eq__" ~self ~@args)) +(defmacro py>= [self & args] `(! "__ge__" ~self ~@args)) +(defmacro pysubscript [self & args] `(! "__getitem__" ~self ~@args)) + +;; z3 stuff +(require-python :from (str (System/getenv "HOME") "/.local/lib/python3.13/site-packages/") '[z3 :as z3]) +(def Int z3/Int) +(def Optimize z3/Optimize) +(def Sum z3/Sum) +(defn apply-formulas [opt [f & fs]] + (when f + (! "add" opt f) + (recur opt fs))) + +;; part 2 +(->> input + (map (fn [{:keys [joltages buttons]}] + (let [opt (Optimize) + bs (->> buttons + count + range + (map #(Int (str "b" %)))) + js (->> joltages + count + range + (map #(Int (str "j" %))))] + (apply-formulas opt (for [idx (range (count joltages))] + (let [jn (nth js idx) + summation-args (->> buttons + (map set) + (map-indexed vector) + (filter #(contains? (second %) idx)) + (map first) + (map #(nth bs %)))] + (py= jn (apply Sum summation-args))))) + (apply-formulas opt (for [[idx joltage] (map-indexed vector joltages)] + (let [jn (nth js idx)] + (py= jn joltage)))) + (apply-formulas opt (for [b bs] (py>= b 0))) + (! "minimize" opt (apply Sum bs)) + (! "check" opt) + (let [model (! "model" opt)] + (apply + + (for [b bs] + (! "as_long" (pysubscript model b)))))))) + (apply +)) +