Skip to content

Commit 73ee693

Browse files
committed
Day 8 - brilliant solution from @JoePittsy
1 parent 7241df6 commit 73ee693

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

docs/day08.md

+70
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,74 @@ in to `find-digits` with the `outputs`, and add together the results. See? Tha
153153
(map (fn [[patterns outputs]] (-> (identify-signals patterns)
154154
(find-digits outputs))))
155155
(apply +)))
156+
```
157+
158+
---
159+
160+
## Alternative
161+
162+
My Twitter friend [@JoePittsy](https://twitter.com/JoePittsy) posted an
163+
[absolutely brilliant solution](https://twitter.com/JoePittsy/status/1468598079569989636/photo/1) to part 2 in Python
164+
which, after I read it multiple times and understood the approach (I don't know Python), I just had to reproduce in
165+
Clojure and share with the world.
166+
167+
He starts with the accepted observation that we can immediately determine the letters in the 1 and 4 signal patterns,
168+
because the 1 is the only pattern with two segments, and the 4 is the only pattern with four segments. Once you have
169+
those two patterns identified, _you don't need to identify the other patterns._
170+
171+
Instead, we can go directly to the four output values, and extract three pieces of data - the length of the output,
172+
the number of overlapping segments with the 1, and the number of overlapping segments with the 4. These three data
173+
elements will uniquely identify each number. For example:
174+
175+
* We know from my solution that there are three numbers that have six segments: 0, 6 and 9.
176+
* Zero has two overlaps with 1 (c and f), and three overlaps with 4 (b, c, and f).
177+
* Six has one overlap with 1 (f) and three overlaps with 4 (b, d, and f).
178+
* Nine has two overlaps with 1 (c and f), and four overlaps with 4 (b, c, d, and f).
179+
* Thus in a partial map of `{[count overlaps-with-one overlaps-with-four] digit}`, we would have at least
180+
`{[6 2 3] 0, [6 1 3] 6, [6 2 4] 9}`.
181+
182+
Taking all ten digits, we create `digit-finder`:
183+
184+
```clojure
185+
(def digit-finder {[6 2 3] 0
186+
[2 2 2] 1
187+
[5 1 2] 2
188+
[5 2 3] 3
189+
[4 2 4] 4
190+
[5 1 3] 5
191+
[6 1 3] 6
192+
[3 2 2] 7
193+
[7 2 4] 8
194+
[6 2 4] 9})
195+
```
196+
197+
So given that we have out lookup table/map, we can create the function `digits`, which takes in the patterns and
198+
outputs, and returns the digits. First, we identify `one` and `four` by finding the distinct 2- and 4-character
199+
in the pattern segments. Then we create a function `digit-keys` using `juxt`, which applies the three functions we
200+
need to form the key of `digit-finder` - the length of the input, the number of segments intersecting `one`, and the
201+
number of segments intersecting `four`. The `(map (comp digit-finder digit-keys) outputs)` line converts each of the
202+
_outputs_ (not patterns) into the key we need, then looks it up in `digit-finder` to get the actual digit. Then
203+
finally we concatenate each digit into a string and parse it as an int to get the actual digit.
204+
205+
```clojure
206+
(defn digits [patterns outputs]
207+
(let [one (first (filter #(= 2 (count %)) patterns))
208+
four (first (filter #(= 4 (count %)) patterns))
209+
digit-keys (juxt count
210+
(comp count (partial set/intersection one))
211+
(comp count (partial set/intersection four)))]
212+
(->> outputs
213+
(map (comp digit-finder digit-keys))
214+
(apply str)
215+
parse-int)))
216+
```
217+
218+
Finally, the revised `part2` function is really simple - parse the input, call `digits` on each line, and add up the
219+
digits. Well done, [@JoePittsy](https://twitter.com/JoePittsy)!
220+
221+
```clojure
222+
(defn part2 [input]
223+
(->> (parse-input input)
224+
(map (partial apply digits))
225+
(apply +)))
156226
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
(ns advent-2021-clojure.day08-joepittsy
2+
(:require
3+
[advent-2021-clojure.day08 :refer [parse-input]]
4+
[advent-2021-clojure.utils :refer [parse-int]]))
5+
6+
; Assume we know the digits for 1 and 4 because they are obvious
7+
; {[digit-length number-of-segments-in-one number-of-segments-in-four] digit}
8+
(def digit-finder {[6 2 3] 0
9+
[2 2 2] 1
10+
[5 1 2] 2
11+
[5 2 3] 3
12+
[4 2 4] 4
13+
[5 1 3] 5
14+
[6 1 3] 6
15+
[3 2 2] 7
16+
[7 2 4] 8
17+
[6 2 4] 9})
18+
19+
(defn digits [patterns outputs]
20+
(let [one (first (filter #(= 2 (count %)) patterns))
21+
four (first (filter #(= 4 (count %)) patterns))
22+
digit-keys (juxt count
23+
(comp count (partial set/intersection one))
24+
(comp count (partial set/intersection four)))]
25+
(->> outputs
26+
(map (comp digit-finder digit-keys))
27+
(apply str)
28+
parse-int)))
29+
30+
(defn part2 [input]
31+
(->> (parse-input input)
32+
(map (partial apply digits))
33+
(apply +)))

0 commit comments

Comments
 (0)