Fork me on GitHub

🧵Day 8 answers thread: post your answers here


(defn procline [l]
  (->> (str/split l #"\|")
       (map str/trim)

(def input
  (->> (util/load-lines "resources/")
       (map procline)))

;; part 1
(->> (map second input)
     (map #(map count %))
     (map #(filter #{7 4 2 3} %))
     (apply concat)
;; => 421

;; part 2
(defn num-common [a b]
  (count (filter (set (sort a)) (sort b))))

(defn decoderfn [gbc]
   (->> (select-keys gbc [2 3 4 7])
        (map first))
   [1 7 4 8]))

(defn nmaps [sharemap gbc decode-em n]
  (let [codeslen-n (get gbc n)
        nums (map sharemap (map decode-em codeslen-n))]
    (zipmap codeslen-n nums)))

(def sharemap5 {'(1 2 2) 2 '(2 3 3) 3 '(1 2 3) 5})
(def sharemap6 {'(1 2 3) 6 '(2 3 4) 9 '(2 3 3) 0})

(defn outval [[signals patterns]]
  (let [gbc (group-by count signals)
        decoder (decoderfn gbc)
        vk147 (map first (vals (select-keys gbc [2 3 4])))
        decode-em (fn [code] (map (partial num-common code) vk147))
        mapper (merge decoder
                      (nmaps sharemap5 gbc decode-em 5)
                      (nmaps sharemap6 gbc decode-em 6))]
    (Integer/parseInt (apply str (map mapper patterns)))))

(reduce + (map outval input)) ;; 986163


Could set/subset? have helped you in your has-all function?

yes 1

;; 0 2 3 5 6 9
  (frequencies '(a b c e f g a c d e g a c d f g a b d f g a b d e f g a b c d f g))
  (into (sorted-map) '{a 6, b 4, c 4, e 3, f 5, g 6, d 5})
  ;; => {a 6, b 4, c 4, d 5, e 3, f 5, g 6}

  ;; 1 4 7 8
  (frequencies '(c f b c d f a c f a b c d e f))
  (into (sorted-map) '{c 4, f 4, b 2, d 2, a 2, e 1})
  ;; => {a 2, b 2, c 4, d 2, e 1, f 4}
looks like it is possible to solve second part using frequencies only, without set operations


yep ) it is definitely possible )

(let [data (:input (first input-data))
        {a 2
         b 4
         c 3
         d 7
         e 5
         f 6} (group-by count data)]
    (ml/matches (ml/matcher '[{?a 2, ?b 2, ?c 4, ?d 2, ?e 1, ?f 4}
                              {?a 6, ?b 4, ?c 4, ?d 5, ?e 3, ?f 5, ?g 6}])
                [(frequencies (flatten (concat a b c d)))
                 (frequencies (flatten (concat e f)))]))
  ;; => ({?a \c, ?b \e, ?c \d, ?d \g, ?e \f, ?f \b, ?g \a})

bananadance 5

@U04V4KLKC amazing! It seems you missed ?g 1 in the first map, or am I wrong?


Oops, you are right)

Callum Oakley14:12:11

brute forced the permutations since there are only 7! (5040) of them takes 700ms

😍 1
💪 3

I took a hybrid approach between freqs and string diffs. I really wanted to try logic programming, but I was way past the Ballmer peak at that point last night.

🍻 2
😂 1
Antonio Bibiano19:12:08

oh man i'm really struggling today


I just had to brute force, even doing it on paper I couldn't see how to deduce the test input to get all the values. Part 1

(defn parser [line]
  (let [[xs ys] (str/split line #" \| ")]
    [(str/split xs #" ") (str/split ys #" ")]))

;; part 1
(let [input (f/read-all-lines-and-parse "puzzle-inputs/2021/day8" parser)]
  (->> input
       (map second)
       (apply concat)
       (map count)
       (filter #(some #{2 3 4 7} #{%}))
       (count))) ; 247


Part 2

(defn parser [line]
  (str/split line #" \| "))

(def numbers {#{\c \f}                1
            #{\a \c \d \e \g}       2
            #{\a \c \d \f \g}       3
            #{\b \c \d \f}          4
            #{\a \b \d \f \g}       5
            #{\a \b \d \e \f \g}    6
            #{\a \c \f}             7
            #{\a \b \c \d \e \f \g} 8
            #{\a \b \c \d \f \g}    9
            #{\a \b \c \e \f \g}    0})

(defn all-combos [xs]
  (combo/permutations xs))

(defn get-replacer [[a b c d e f g]]
  {"a" a, "b" b, "c" c, "d" d, "e" e, "f" f, "g" g})

(defn decoded->int [decoded]
  (Integer/parseInt (str/join "" decoded)))

(defn to-display-num [num]
  (map set (str/split num #"\s")))

(defn solve-line [[input to-solve]]
  (loop [[perm & rest] (all-combos ["a" "b" "c" "d" "e" "f" "g"])]
    (if (nil? rest)
      (let [replacements (get-replacer perm)
            input'       (str/replace input #"a|b|c|d|e|f|g" replacements)
            nums         (to-display-num input')]
        (if (every? numbers nums)
          (->> (str/replace to-solve #"a|b|c|d|e|f|g" replacements)
               (map numbers)
          (recur rest))))))

(let [input (f/read-all-lines-and-parse "puzzle-inputs/2021/day8" parser)]
  (->> input 
       (map solve-line)
       (reduce +)))


Using mainly exclusions separate based on the old clojure-contrib, not sure if there is a replacement

Vincent Cantin21:12:20

After running out of heap memory by trying to solve it via constraint propagation via recursion, I solved it in this simpler way:

  (:require [ :as io]
            [clojure.string :as str]
            [clojure.set :as set]
            [aoc.util :refer [comp->]]))

(def input
  (->> (io/resource "2021/day8.txt")
       (mapv (fn [line]
               (vec (re-seq #"\w+" line))))))

;; Part 1
(->> input
     (mapcat (fn [line]
               (subvec line 10)))
     (filter (comp-> count #{2 4 3 7}))

;; Part 2
(defn solve [line]
  (let [digits (subvec line 0 10)
        secrets (subvec line 10)
        count->digits (group-by count (mapv set digits))
        mapping {1 (-> 2 count->digits first)
                 7 (-> 3 count->digits first)
                 4 (-> 4 count->digits first)
                 8 (-> 7 count->digits first)}
        mapping (assoc mapping 3 (-> 5 count->digits
                                     (->> (filter #(set/superset? % (mapping 7))))
        mapping (assoc mapping 2 (-> 5 count->digits
                                     (->> (remove #{(mapping 3)}))
                                     (->> (filter #(= (count (set/intersection % (mapping 4))) 2)))
        mapping (assoc mapping 5 (-> 5 count->digits
                                     (->> (remove #{(mapping 3) (mapping 2)}))
        mapping (assoc mapping 9 (-> 6 count->digits
                                     (->> (filter #(set/superset? % (mapping 4))))
        mapping (assoc mapping 0 (-> 6 count->digits
                                     (->> (remove #{(mapping 9)}))
                                     (->> (filter #(set/superset? % (mapping 1))))
        mapping (assoc mapping 6 (-> 6 count->digits
                                     (->> (remove #{(mapping 9) (mapping 0)}))
        rmapping (into {} (map (fn [[k v]] [v k])) mapping)]
    (->> secrets
         (map set)
         (map rmapping)
         (apply str)

(transduce (map solve) + input)
; => 936117

Andrew Byala21:12:35

I'm feeling ok about my solution. I threw together a few letfn definitions to make my solution for each signal a bit easier on the eyes. •

👏 1
Andrew Byala21:12:09

I'm making my way through the code, but I really like @U016C0EGHUN' parsing logic, calling (map #(partition-by #{"|"} %) lines) to split both sides of the input line. I never thought of using a set of the delimiter to split the data sides (`false`) from the delimiter itself (`true`). And I hope you liked that little compliment, because I don't understand core.logic and it looks rather intimidating!

Antonio Bibiano22:12:13

done, a mix of segment frequency and a check with the number 4 to solve one ambiguity..

Sam Adams22:12:39

Thanks Andrew! And that makes two of us 😁

karol22:12:07 really enjoyed today task. the first part was quite a breeze, the second one took more time. The core idea was that I made a function that given the input creates a possible substition map like:

{\a #{\c \f}
 \b #{\g \d \c}}
etc. which was then just the case of substituting code looking for easy substitutions (where there is only one possibility in set) and removing them from other possibilities. If there were no easy substitution the code would make a "guess" and fork on possible substitutions based on that guess I made an assumption that until substitution is solved or there is an easy pick (one which has only one possibility to substitute) there will be always at least one substitution that have only 2 possibilities and then did a fork on each of those substitutions when looking up for solutions. It proved to be enough but would really like to later modify my code so when it has to guess it will fork on the lowest guess count needed Fixing this assumption went much faster than I thought, so the code now if there is no easy substitution selects the ones with the lowest guesses needed and calls fn again recursively with each guess substituted in possibilities

Sam Adams22:12:40

@U01HHBJ56J1 your writeup flows very smoothly, and I like that the your code bits stand on their own with the commentary beforehand — I should not have mixed mine together in today’s blog

Björn Ebbinghaus23:12:50

I think I have a good solution. Just based on set cardinality / intersection / difference 5ms for task 2 with slurp + parse + execute in babashka

👍 3

after some fumbling around I went for brute force in this one (takes about 15sec):


@U1UHFHABV: you can use (let [{matches true misses false} (group-by f all)] ,,,) for separate

Vincent Cantin05:12:03

@U067R559Q I think you deserve the achievement title "Mr. frequencies" 🙂

😅 1

honestly relieved to see everyone else's solutions aren't like 10 lines


day8 spoilers


does it make sense to use core.logic?


Not really, or at least not in a naive way. I have a in it but it is horribly slow.

👀 1

Short animation how algorithm slowly converges on answer

👍 1
🎉 1
👏 1
💯 1

Brightness of yellow represents certanty in output, brighter green is processed input


Looks very interesting. Mind sharing the code? (Or describing the algorithm?)


Nevermind. Found it on your github 😃

👍 1