Skip to content

Commit f61fde7

Browse files
committed
Day 19: Solve part 1 and 2
1 parent eee81b3 commit f61fde7

File tree

3 files changed

+131
-13
lines changed

3 files changed

+131
-13
lines changed

Diff for: src/advent_of_code_2023/day19.clj

+109-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,114 @@
22
(:require
33
[advent-of-code-2023.utils.string :as u]))
44

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)]))
615

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}))
824

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]})))

Diff for: src/advent_of_code_2023/utils/graph.clj

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
visited visited
2323
path-val (path-fn)]
2424
(let [[c] (first q)
25-
q (if (seq q) (pop q) nil)]
25+
q (when (seq q) (pop q))]
2626
(cond
2727
(continue? c) (let [nz (neighbours c visited)]
2828
(recur (reduce assoc-to-priority-map q nz)

Diff for: test/advent_of_code_2023/day19_test.clj

+21-9
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,38 @@
44
[advent-of-code-2023.day19 :refer :all]
55
[advent-of-code-2023.test-utils :as tu]))
66

7-
(defonce ^:private example-input (parse-input ""))
7+
(defonce ^:private example-input (parse-input "px{a<2006:qkq,m>2090:A,rfg}
8+
pv{a>1716:R,A}
9+
lnx{m>1548:A,A}
10+
rfg{s<537:gd,x>2440:R,A}
11+
qs{s>3448:A,lnx}
12+
qkq{x<1416:A,crn}
13+
crn{x>2662:A,R}
14+
in{s<1351:px,qqz}
15+
qqz{s>2770:qs,m<1801:hdj,R}
16+
gd{a>3333:R,R}
17+
hdj{m>838:A,pv}
18+
19+
{x=787,m=2655,a=1222,s=2876}
20+
{x=1679,m=44,a=2067,s=496}
21+
{x=2036,m=264,a=79,s=2244}
22+
{x=2461,m=1339,a=466,s=291}
23+
{x=2127,m=1623,a=2188,s=1013}"))
824

925
(def ^:private input (parse-input (tu/slurp-input "resources/day19.txt")))
1026

1127
(deftest day19-1-example-test
1228
(testing "day19-1 example"
13-
(is (= nil
14-
(day19-1 example-input)))))
29+
(is (= 19114 (day19-1 example-input)))))
1530

1631
(deftest day19-1-test
1732
(testing "day19-1"
18-
(is (= nil
19-
(day19-1 input)))))
33+
(is (= 397134 (day19-1 input)))))
2034

2135
(deftest day19-2-example-test
2236
(testing "day19-2 example"
23-
(is (= nil
24-
(day19-2 example-input)))))
37+
(is (= 167409079868000 (day19-2 example-input)))))
2538

2639
(deftest day19-2-test
2740
(testing "day19-2"
28-
(is (= nil
29-
(day19-2 input)))))
41+
(is (= 127517902575337 (day19-2 input)))))

0 commit comments

Comments
 (0)