From f61fde78affb5db246e6e1a7e2115212f513ed1e Mon Sep 17 00:00:00 2001 From: Nikita Hasert Date: Tue, 19 Dec 2023 21:38:47 +0100 Subject: [PATCH] Day 19: Solve part 1 and 2 --- src/advent_of_code_2023/day19.clj | 112 +++++++++++++++++++++++- src/advent_of_code_2023/utils/graph.clj | 2 +- test/advent_of_code_2023/day19_test.clj | 30 +++++-- 3 files changed, 131 insertions(+), 13 deletions(-) diff --git a/src/advent_of_code_2023/day19.clj b/src/advent_of_code_2023/day19.clj index 88ee110..0e6e501 100644 --- a/src/advent_of_code_2023/day19.clj +++ b/src/advent_of_code_2023/day19.clj @@ -2,8 +2,114 @@ (:require [advent-of-code-2023.utils.string :as u])) -(defn parse-input [input]) +(defn- parse-condition [condition] + (let [[p fun comp] (re-seq #"\d+|[^<>=]+|[<>=]" condition)] + [(cond + fun (let [fun (resolve (read-string fun)) + y (Long/parseLong comp)] + (fn [part] (fun (part p) y))) + (= p "A") (constantly "A") + (= p "R") (constantly "R") + :else (constantly p)) + fun p (if fun (Long/parseLong comp) comp)])) -(defn day19-1 [parsed-input]) +(defn- parse-rule [rule] + (let [[condition target] (u/split-sections rule #":") + [cond-fun fun p comp] (parse-condition condition)] + {:condition cond-fun + :fun fun + :p p + :comp comp + :target target})) -(defn day19-2 [parsed-input]) \ No newline at end of file +(defn- parse-rules [rules] + (when rules + (mapv parse-rule (u/split-sections rules #",")))) + +(defn- parse-parts [parts] + (when parts + (mapv (fn [part-ratings] + (transduce + (map (fn [[k v]] [k (Long/parseLong v)])) + conj {} (partition-all 2 (re-seq #"[a-z]+|\d+" part-ratings)))) + (u/split-sections parts u/line-endings)))) + +(defn- parse-line [line] + (let [[workflow rules] + (u/split-sections line #"\{|\}")] + {workflow (parse-rules rules)})) + +(defn parse-input [input] + (when input + (let [[wf-input parts] (u/split-sections input)] + {:workflows (transduce + (map parse-line) + merge (u/split-sections wf-input u/line-endings)) + :part-ratings (parse-parts parts)}))) + +(defn- run-workflow [workflow part-rating] + (loop [[{:keys [condition target]} & wf] workflow] + (let [result (condition part-rating)] + (cond + (string? result) result + result target + :else (recur wf))))) + +(defn- workflow [workflows] + (fn [part-rating workflow] + (let [next-workflow (run-workflow (workflows workflow) part-rating)] + (cond + (#{"R" "A"} next-workflow) (= "A" next-workflow) + (string? next-workflow) (recur part-rating next-workflow) + (keyword? next-workflow) next-workflow)))) + +(defn day19-1 [{:keys [workflows part-ratings]}] + (transduce + (comp + (filter #((workflow workflows) % "in")) + (map (juxt #(get % "x") #(get % "m") #(get % "a") #(get % "s"))) + (map (partial reduce +))) + + part-ratings)) + +(defn- shorten-range-left [[^long a ^long b] ^long c] + [(max a c) b]) + +(defn- shorten-range-right [[^long a ^long b] ^long c] + [a (min b c)]) + +(defn- split-range [part-rating-ranges fun p ^long comp] + (condp = fun + "<" [(update part-rating-ranges p shorten-range-left comp) + (update part-rating-ranges p shorten-range-right (dec comp))] + ">" [(update part-rating-ranges p shorten-range-right comp) + (update part-rating-ranges p shorten-range-left (inc comp))])) + +(defn combinations-count [ranges] + (transduce + (map (fn [[^long mi ^long ma]] (- (inc ma) mi))) + * (vals ranges))) + +(defn- valid-input-ranges [wf-name + [{:keys [fun p comp target]} & wfs] + workflows + part-rating-ranges] + (cond + (= wf-name "A") [part-rating-ranges] + (= wf-name "R") [] + fun (let [[wf-false wf-true] (split-range part-rating-ranges fun p comp)] + (into (valid-input-ranges wf-name wfs + workflows wf-false) + (valid-input-ranges target (workflows target) + workflows wf-true))) + p (recur p (workflows p) workflows part-rating-ranges))) + +(defn day19-2 [{:keys [workflows]}] + (transduce + (map combinations-count) + + (valid-input-ranges "in" + (workflows "in") + workflows + {"x" [1 4000] + "m" [1 4000] + "a" [1 4000] + "s" [1 4000]}))) \ No newline at end of file diff --git a/src/advent_of_code_2023/utils/graph.clj b/src/advent_of_code_2023/utils/graph.clj index d81bd71..ecabb81 100644 --- a/src/advent_of_code_2023/utils/graph.clj +++ b/src/advent_of_code_2023/utils/graph.clj @@ -22,7 +22,7 @@ visited visited path-val (path-fn)] (let [[c] (first q) - q (if (seq q) (pop q) nil)] + q (when (seq q) (pop q))] (cond (continue? c) (let [nz (neighbours c visited)] (recur (reduce assoc-to-priority-map q nz) diff --git a/test/advent_of_code_2023/day19_test.clj b/test/advent_of_code_2023/day19_test.clj index b91d52c..eadd9a0 100644 --- a/test/advent_of_code_2023/day19_test.clj +++ b/test/advent_of_code_2023/day19_test.clj @@ -4,26 +4,38 @@ [advent-of-code-2023.day19 :refer :all] [advent-of-code-2023.test-utils :as tu])) -(defonce ^:private example-input (parse-input "")) +(defonce ^:private example-input (parse-input "px{a<2006:qkq,m>2090:A,rfg} +pv{a>1716:R,A} +lnx{m>1548:A,A} +rfg{s<537:gd,x>2440:R,A} +qs{s>3448:A,lnx} +qkq{x<1416:A,crn} +crn{x>2662:A,R} +in{s<1351:px,qqz} +qqz{s>2770:qs,m<1801:hdj,R} +gd{a>3333:R,R} +hdj{m>838:A,pv} + +{x=787,m=2655,a=1222,s=2876} +{x=1679,m=44,a=2067,s=496} +{x=2036,m=264,a=79,s=2244} +{x=2461,m=1339,a=466,s=291} +{x=2127,m=1623,a=2188,s=1013}")) (def ^:private input (parse-input (tu/slurp-input "resources/day19.txt"))) (deftest day19-1-example-test (testing "day19-1 example" - (is (= nil - (day19-1 example-input))))) + (is (= 19114 (day19-1 example-input))))) (deftest day19-1-test (testing "day19-1" - (is (= nil - (day19-1 input))))) + (is (= 397134 (day19-1 input))))) (deftest day19-2-example-test (testing "day19-2 example" - (is (= nil - (day19-2 example-input))))) + (is (= 167409079868000 (day19-2 example-input))))) (deftest day19-2-test (testing "day19-2" - (is (= nil - (day19-2 input))))) + (is (= 127517902575337 (day19-2 input)))))