1
1
(ns advent-of-code-2023.day10
2
2
(:require
3
- [advent-of-code-2023.utils.string :as u]))
3
+ [clojure.set :as set]
4
+ [advent-of-code-2023.utils.graph :as g]))
4
5
5
- (defn parse-input [input])
6
+ (defn parse-input [input]
7
+ (let [{:keys [positions] :as m} (g/parse-positional-map input identity)]
8
+ (assoc m :start
9
+ (ffirst (filterv (comp #{\S} second) positions)))))
6
10
7
- (defn day10-1 [parsed-input])
11
+ (defonce ^:private valid-state?
12
+ #{[\| :north ]
13
+ [\| :south ]
14
+ [\- :east ]
15
+ [\- :west ]
16
+ [\L :west ]
17
+ [\L :south ]
18
+ [\J :east ]
19
+ [\J :south ]
20
+ [\7 :north ]
21
+ [\7 :east ]
22
+ [\F :west ]
23
+ [\F :north ]
24
+ [\S :north ]
25
+ [\S :east ]
26
+ [\S :south ]
27
+ [\S :west ]})
8
28
9
- (defn day10-2 [parsed-input])
29
+ (defn- next-position [[^long x ^long y] direction]
30
+ (condp = direction
31
+ :north [x (dec y)]
32
+ :east [(inc x) y]
33
+ :south [x (inc y)]
34
+ :west [(dec x) y]))
35
+
36
+ (defn- next-direction [pipe direction]
37
+ (condp = [pipe direction]
38
+ [\| :north ] :north
39
+ [\| :south ] :south
40
+ [\- :east ] :east
41
+ [\- :west ] :west
42
+ [\L :west ] :north
43
+ [\L :south ] :east
44
+ [\J :east ] :north
45
+ [\J :south ] :west
46
+ [\7 :north ] :west
47
+ [\7 :east ] :south
48
+ [\F :west ] :south
49
+ [\F :north ] :east
50
+ [\S :north ] :north
51
+ [\S :east ] :east
52
+ [\S :south ] :south
53
+ [\S :west ] :west ))
54
+
55
+ (defn- next-pipe-pos-fn [{:keys [positions]}]
56
+ (fn next-pipe-pos [[[^long x ^long y] _ direction] visited]
57
+ (let [next-pos (next-position [x y] direction)
58
+ next-pipe (positions next-pos)]
59
+ (cond
60
+ (visited next-pos) nil
61
+ (valid-state? [next-pipe direction]) [next-pos next-pipe (next-direction next-pipe direction)]
62
+ :else nil ))))
63
+
64
+ (defn try-loop [start-state continue? next-pipe-pos]
65
+ (loop [pipe-pos start-state
66
+ visited #{}
67
+ path []]
68
+ (cond
69
+ (continue? pipe-pos) (when-let [np (next-pipe-pos pipe-pos visited)]
70
+ (recur np
71
+ (conj visited (first np))
72
+ (conj path pipe-pos)))
73
+ :else (conj path pipe-pos))))
74
+
75
+ (defn- continue?-fn [direction]
76
+ (fn continue? [[n v d]]
77
+ (or (and n (not= v \S))
78
+ (= direction d))))
79
+
80
+ (defn- find-loop [{:keys [start] :as m}]
81
+ (first
82
+ (transduce
83
+ (comp
84
+ (map
85
+ (fn [direction]
86
+ (try-loop [start \S direction]
87
+ (continue?-fn direction)
88
+ (next-pipe-pos-fn m))))
89
+ (remove nil?))
90
+ conj []
91
+ [:north :east :south :west ])))
92
+
93
+
94
+ (defn day10-1 [m]
95
+ (-> m
96
+ find-loop
97
+ count
98
+ dec ; Subtract the start point that appears twice
99
+ (quot ,,, 2 )))
100
+
101
+ (defn- enclosed-candidates [loop ]
102
+ (let [loop-pos (set (mapv first loop ))
103
+ x-pos (mapv first loop-pos)
104
+ y-pos (mapv second loop-pos)
105
+ min-x (reduce min x-pos)
106
+ min-y (reduce min y-pos)
107
+ max-x (reduce max x-pos)
108
+ max-y (reduce max y-pos)]
109
+ (for [x (range min-x (inc ^long max-x))
110
+ y (range min-y (inc ^long max-y))
111
+ :when ((complement loop-pos) [x y])]
112
+ [x y])))
113
+
114
+ (defonce ^:private valid-neighbouring-pipes
115
+ {[\| :north ] #{\| \7 \F}
116
+ [\| :south ] #{\| \L \J}
117
+ [\- :east ] #{\- \J \7 }
118
+ [\- :west ] #{\- \L \F}
119
+ [\L :west ] #{\| \7 \F}
120
+ [\L :south ] #{\- \J \7 }
121
+ [\J :east ] #{\| \7 \F}
122
+ [\J :south ] #{\- \L \F}
123
+ [\7 :north ] #{\- \L \F}
124
+ [\7 :east ] #{\| \L \J}
125
+ [\F :west ] #{\| \L \J}
126
+ [\F :north ] #{\- \J \7 }
127
+ [\S :north ] #{\| \7 \F}
128
+ [\S :east ] #{\- \J \7 }
129
+ [\S :south ] #{\| \L \J}
130
+ [\S :west ] #{\- \L \F}})
131
+
132
+ (defn- determine-separators [loop ]
133
+ (let [sec (rest (second loop ))
134
+ sec-last (rest (second (reverse loop )))
135
+ s (->> [sec sec-last]
136
+ (mapv valid-neighbouring-pipes)
137
+ (apply set/intersection)
138
+ vec
139
+ first)]
140
+ (if (#{\L \J \|} s)
141
+ #{\L \S \J \|}
142
+ #{\L \J \|})))
143
+
144
+ (defn- loop-map [loop ]
145
+ (reduce (fn [m [pos v _]] (assoc m pos v))
146
+ {}
147
+ loop ))
148
+
149
+ (defn- enclosed?-fn [loop ]
150
+ (let [sep (determine-separators loop )
151
+ loop-m (loop-map loop )]
152
+ (fn enclosed? [[x y]]
153
+ ; ; Rasterization: We count how many times we cross a pipe going west
154
+ ; ; from a possible enclosed point
155
+ ; ; If it is an odd amount -> enclosed
156
+ ; ; This can probably be done a lot smarter to avoid a lot of duplicate work for a given row
157
+ (->> x
158
+ range
159
+ (transduce
160
+ (comp
161
+ (map (fn [xx] [xx y]))
162
+ (map loop-m)
163
+ (filter sep))
164
+ conj [])
165
+ count
166
+ odd?))))
167
+
168
+ (defn day10-2 [pipe-map]
169
+ (let [loop (find-loop pipe-map)
170
+ enclosed-cand (enclosed-candidates loop )]
171
+ (count (filterv (enclosed?-fn loop ) enclosed-cand))))
0 commit comments