2
2
(:require
3
3
[advent-of-code-2023.utils.string :as u]))
4
4
5
- (defn parse-input [input])
5
+ (defn- parse-condition [condition]
6
+ (let [[p fun comp] (re-seq #"\d +|[^<>=]+|[<>=]" condition)]
7
+ [(cond
8
+ fun (let [fun (resolve (read-string fun))
9
+ y (Long/parseLong comp)]
10
+ (fn [part] (fun (part p) y)))
11
+ (= p " A" ) (constantly " A" )
12
+ (= p " R" ) (constantly " R" )
13
+ :else (constantly p))
14
+ fun p (if fun (Long/parseLong comp) comp)]))
6
15
7
- (defn day19-1 [parsed-input])
16
+ (defn- parse-rule [rule]
17
+ (let [[condition target] (u/split-sections rule #":" )
18
+ [cond-fun fun p comp] (parse-condition condition)]
19
+ {:condition cond-fun
20
+ :fun fun
21
+ :p p
22
+ :comp comp
23
+ :target target}))
8
24
9
- (defn day19-2 [parsed-input])
25
+ (defn- parse-rules [rules]
26
+ (when rules
27
+ (mapv parse-rule (u/split-sections rules #"," ))))
28
+
29
+ (defn- parse-parts [parts]
30
+ (when parts
31
+ (mapv (fn [part-ratings]
32
+ (transduce
33
+ (map (fn [[k v]] [k (Long/parseLong v)]))
34
+ conj {} (partition-all 2 (re-seq #"[a-z]+|\d +" part-ratings))))
35
+ (u/split-sections parts u/line-endings))))
36
+
37
+ (defn- parse-line [line]
38
+ (let [[workflow rules]
39
+ (u/split-sections line #"\{ |\} " )]
40
+ {workflow (parse-rules rules)}))
41
+
42
+ (defn parse-input [input]
43
+ (when input
44
+ (let [[wf-input parts] (u/split-sections input)]
45
+ {:workflows (transduce
46
+ (map parse-line)
47
+ merge (u/split-sections wf-input u/line-endings))
48
+ :part-ratings (parse-parts parts)})))
49
+
50
+ (defn- run-workflow [workflow part-rating]
51
+ (loop [[{:keys [condition target]} & wf] workflow]
52
+ (let [result (condition part-rating)]
53
+ (cond
54
+ (string? result) result
55
+ result target
56
+ :else (recur wf)))))
57
+
58
+ (defn- workflow [workflows]
59
+ (fn [part-rating workflow]
60
+ (let [next-workflow (run-workflow (workflows workflow) part-rating)]
61
+ (cond
62
+ (#{" R" " A" } next-workflow) (= " A" next-workflow)
63
+ (string? next-workflow) (recur part-rating next-workflow)
64
+ (keyword? next-workflow) next-workflow))))
65
+
66
+ (defn day19-1 [{:keys [workflows part-ratings]}]
67
+ (transduce
68
+ (comp
69
+ (filter #((workflow workflows) % " in" ))
70
+ (map (juxt #(get % " x" ) #(get % " m" ) #(get % " a" ) #(get % " s" )))
71
+ (map (partial reduce +)))
72
+ + part-ratings))
73
+
74
+ (defn- shorten-range-left [[^long a ^long b] ^long c]
75
+ [(max a c) b])
76
+
77
+ (defn- shorten-range-right [[^long a ^long b] ^long c]
78
+ [a (min b c)])
79
+
80
+ (defn- split-range [part-rating-ranges fun p ^long comp]
81
+ (condp = fun
82
+ " <" [(update part-rating-ranges p shorten-range-left comp)
83
+ (update part-rating-ranges p shorten-range-right (dec comp))]
84
+ " >" [(update part-rating-ranges p shorten-range-right comp)
85
+ (update part-rating-ranges p shorten-range-left (inc comp))]))
86
+
87
+ (defn combinations-count [ranges]
88
+ (transduce
89
+ (map (fn [[^long mi ^long ma]] (- (inc ma) mi)))
90
+ * (vals ranges)))
91
+
92
+ (defn- valid-input-ranges [wf-name
93
+ [{:keys [fun p comp target]} & wfs]
94
+ workflows
95
+ part-rating-ranges]
96
+ (cond
97
+ (= wf-name " A" ) [part-rating-ranges]
98
+ (= wf-name " R" ) []
99
+ fun (let [[wf-false wf-true] (split-range part-rating-ranges fun p comp)]
100
+ (into (valid-input-ranges wf-name wfs
101
+ workflows wf-false)
102
+ (valid-input-ranges target (workflows target)
103
+ workflows wf-true)))
104
+ p (recur p (workflows p) workflows part-rating-ranges)))
105
+
106
+ (defn day19-2 [{:keys [workflows]}]
107
+ (transduce
108
+ (map combinations-count)
109
+ + (valid-input-ranges " in"
110
+ (workflows " in" )
111
+ workflows
112
+ {" x" [1 4000 ]
113
+ " m" [1 4000 ]
114
+ " a" [1 4000 ]
115
+ " s" [1 4000 ]})))
0 commit comments