Skip to content

Commit fe5b171

Browse files
committed
Day 10: Solve part 1 and 2
1 parent a01e2b1 commit fe5b171

File tree

3 files changed

+216
-17
lines changed

3 files changed

+216
-17
lines changed

Diff for: src/advent_of_code_2023/day10.clj

+166-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,171 @@
11
(ns advent-of-code-2023.day10
22
(:require
3-
[advent-of-code-2023.utils.string :as u]))
3+
[clojure.set :as set]
4+
[advent-of-code-2023.utils.graph :as g]))
45

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)))))
610

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

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

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,9 @@
3030
(path-fn path-val c))
3131
:else (path-fn path-val c)))))
3232

33-
(defn bfs [start-state end-val neighbours visited-state path-fn]
33+
(defn bfs [start-state continue? neighbours visited-state path-fn]
3434
(pruning-bfs start-state neighbours visited-state
35-
(constantly 0)
36-
(fn [[n v]] (and n (not= v end-val)))
37-
path-fn))
35+
(constantly 0) continue? path-fn))
3836

3937
;;; Positional graphs
4038

Diff for: test/advent_of_code_2023/day10_test.clj

+48-9
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,65 @@
44
[advent-of-code-2023.day10 :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 ".....
8+
.S-7.
9+
.|.|.
10+
.L-J.
11+
....."))
12+
13+
(defonce ^:private example-input-2 (parse-input "..F7.
14+
.FJ|.
15+
SJ.L7
16+
|F--J
17+
LJ..."))
818

919
(def ^:private input (parse-input (tu/slurp-input "resources/day10.txt")))
1020

1121
(deftest day10-1-example-test
1222
(testing "day10-1 example"
13-
(is (= nil
14-
(day10-1 example-input)))))
23+
(is (= 4 (day10-1 example-input)))))
24+
25+
(deftest day10-1-example-2-test
26+
(testing "day10-1 example 2"
27+
(is (= 8 (day10-1 example-input-2)))))
1528

1629
(deftest day10-1-test
1730
(testing "day10-1"
18-
(is (= nil
19-
(day10-1 input)))))
31+
(is (= 6968 (day10-1 input)))))
2032

2133
(deftest day10-2-example-test
2234
(testing "day10-2 example"
23-
(is (= nil
24-
(day10-2 example-input)))))
35+
(is (= 1 (day10-2 example-input)))))
36+
37+
(defonce ^:private example-input-3 (parse-input "...........
38+
.S-------7.
39+
.|F-----7|.
40+
.||.....||.
41+
.||.....||.
42+
.|L-7.F-J|.
43+
.|..|.|..|.
44+
.L--J.L--J.
45+
..........."))
46+
47+
(deftest day10-2-example-3-test
48+
(testing "day10-2 example 3"
49+
(is (= 4 (day10-2 example-input-3)))))
50+
51+
(defonce ^:private example-input-4 (parse-input "FF7FSF7F7F7F7F7F---7
52+
L|LJ||||||||||||F--J
53+
FL-7LJLJ||||||LJL-77
54+
F--JF--7||LJLJ7F7FJ-
55+
L---JF-JLJ.||-FJLJJ7
56+
|F|F-JF---7F7-L7L|7|
57+
|FFJF7L7F-JF7|JL---7
58+
7-L-JL7||F7|L7F-7F7|
59+
L.L7LFJ|||||FJL7||LJ
60+
L7JLJL-JLJLJL--JLJ.L"))
61+
62+
(deftest day10-2-example-4-test
63+
(testing "day10-2 example 4"
64+
(is (= 10 (day10-2 example-input-4)))))
2565

2666
(deftest day10-2-test
2767
(testing "day10-2"
28-
(is (= nil
29-
(day10-2 input)))))
68+
(is (= 413 (day10-2 input)))))

0 commit comments

Comments
 (0)