Skip to content

Commit 7228567

Browse files
committed
Advent of Code Day 19 part 1
1 parent ce60d60 commit 7228567

File tree

6 files changed

+150
-0
lines changed

6 files changed

+150
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{:lint-as {taoensso.tufte/defnp clojure.core/defn}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{:hooks {:analyze-call {taoensso.encore/defalias taoensso.encore/defalias}}}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
(ns taoensso.encore
2+
(:require
3+
[clj-kondo.hooks-api :as hooks]))
4+
5+
(defn defalias [{:keys [node]}]
6+
(let [[sym-raw src-raw] (rest (:children node))
7+
src (if src-raw src-raw sym-raw)
8+
sym (if src-raw
9+
sym-raw
10+
(symbol (name (hooks/sexpr src))))]
11+
{:node (with-meta
12+
(hooks/list-node
13+
[(hooks/token-node 'def)
14+
(hooks/token-node (hooks/sexpr sym))
15+
(hooks/token-node (hooks/sexpr src))])
16+
(meta src))}))
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
(ns day-19-1
2+
(:require
3+
[clojure.java.io :as io]
4+
[clojure.string :as str]
5+
[clojure.test :as t :refer [deftest is]]
6+
[instaparse.core :as ic]
7+
[instaparse.transform :as it]
8+
[taoensso.tufte :as tufte :refer [defnp p]]))
9+
10+
;; DRAMATIS PERSONAE
11+
;; xn .... an fsm transition
12+
;; xf .... a transform
13+
;; m ..... a part rating
14+
15+
(defn make-guard-predicate
16+
[k op int]
17+
;; do a code-as-data thing so we can examine in the pipeline; otherwise
18+
;; anonymous fns are opaque and poor for debugging
19+
(let [sexp (list 'fn '[m] (list 'p ::guard
20+
(if (= op '<)
21+
;; swap so always <
22+
(list '< (list k 'm) int)
23+
(list '< int (list k 'm)))))
24+
f (eval sexp)]
25+
;; stash source in meta so it's examinable
26+
(with-meta f {::guard-source sexp})))
27+
28+
(def parse-transitions
29+
(let [grammar "workflow = state transitions
30+
transitions = <'{'> transition (<','> transition)* <'}'>
31+
<transition> = guarded-xn | unguarded-xn
32+
guarded-xn = (guard <':'> state)
33+
unguarded-xn = state
34+
guard = k op int
35+
state = #'[a-z]+' | 'A' | 'R'
36+
k = #'[a-z]+'
37+
op = '<' | '>'
38+
int = #'\\d+'"
39+
xfs {:int parse-long
40+
:op symbol
41+
:k keyword
42+
:state keyword
43+
:unguarded-xn (fn [k] {:to k})
44+
:guard make-guard-predicate
45+
:guarded-xn (fn [guard k] {:guard guard :to k})
46+
:transitions vector
47+
:workflow vector}]
48+
(comp (partial it/transform xfs)
49+
(ic/parser grammar))))
50+
51+
(def parse-rating
52+
(let [grammar "rating = <'{'> entry (<','> entry)* <'}'>
53+
entry = k <'='> n
54+
k = #'[a-z]+'
55+
n = #'\\d+'"
56+
xfs {:n parse-long
57+
:k keyword
58+
:entry vector
59+
:rating (fn [& kns] (into {} kns))}]
60+
(comp (partial it/transform xfs)
61+
(ic/parser grammar))))
62+
63+
(defnp read-input [path]
64+
(with-open [r (io/reader path)]
65+
(let [[raw-xns _ raw-ratings] (doall (partition-by str/blank? (line-seq r)))]
66+
{:xns (into {} (map parse-transitions) raw-xns)
67+
:ms (map parse-rating raw-ratings)})))
68+
69+
(defnp pick-transition
70+
"Find first transition in ordered transitions xns where the guard on m
71+
passes."
72+
[xns m]
73+
(reduce (fn [_ {guard :guard :or {guard (constantly true)} :as xn}]
74+
(let [use? (guard m)]
75+
#_(prn (-> guard meta ::guard-source) use? to)
76+
(when use? (reduced xn))))
77+
nil
78+
xns))
79+
80+
(defnp step-fsm [fsm]
81+
(let [m (:m fsm)
82+
state (:state fsm)
83+
xns (get-in fsm [:xns state])
84+
xn (pick-transition xns m)
85+
next-state (:to xn)]
86+
(assoc fsm :state next-state)))
87+
88+
(defn run-fsm
89+
"Keep following transitions state until we hit an :A or an :R."
90+
[terminal? fsm]
91+
(let [fsm' (step-fsm fsm)]
92+
(if (terminal? (:state fsm')) fsm' (recur terminal? fsm'))))
93+
94+
(defnp part-1 [{:keys [xns ms]}]
95+
(let [fsm {:state :in :xns xns}
96+
xf (comp
97+
(map (fn [m] (assoc fsm :m m)))
98+
(map (fn [fsm] (run-fsm #{:A :R} fsm)))
99+
(filter (fn [fsm] (= :A (:state fsm))))
100+
(map :m)
101+
(mapcat vals))]
102+
(transduce xf + ms)))
103+
104+
(deftest test-part-1 (is (= 19114 (part-1 (read-input "test.txt")))))
105+
106+
(comment
107+
(tufte/add-basic-println-handler! {})
108+
(tufte/profile {} (time (part-1 (read-input "test.txt"))))
109+
(tufte/profile {} (time (part-1 (read-input "input.txt"))))) ; 330820
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{:paths ["."]
2+
:deps {com.taoensso/tufte {:mvn/version "2.6.3"}
3+
criterium/criterium {:mvn/version "0.4.6"}
4+
instaparse/instaparse {:mvn/version "1.4.12"}
5+
net.cgrand/xforms {:mvn/version "0.19.5"}
6+
org.clojure/math.combinatorics {:mvn/version "0.2.0"}}}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
px{a<2006:qkq,m>2090:A,rfg}
2+
pv{a>1716:R,A}
3+
lnx{m>1548:A,A}
4+
rfg{s<537:gd,x>2440:R,A}
5+
qs{s>3448:A,lnx}
6+
qkq{x<1416:A,crn}
7+
crn{x>2662:A,R}
8+
in{s<1351:px,qqz}
9+
qqz{s>2770:qs,m<1801:hdj,R}
10+
gd{a>3333:R,R}
11+
hdj{m>838:A,pv}
12+
13+
{x=787,m=2655,a=1222,s=2876}
14+
{x=1679,m=44,a=2067,s=496}
15+
{x=2036,m=264,a=79,s=2244}
16+
{x=2461,m=1339,a=466,s=291}
17+
{x=2127,m=1623,a=2188,s=1013}

0 commit comments

Comments
 (0)