aboutsummaryrefslogtreecommitdiff
path: root/2021/day8/08b.clj
blob: 067f8025a38571641069b2fad7ed2bd23c6df805 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
(ns day-8)

(require '[clojure.string :as str])
(require '[clojure.set :as set])

(def ALPHABET #{\a \b \c \d \e \f \g})
(defn decodeWith [signal mapping]
  (cond (every? (fn [x] (contains? mapping x)) signal) (set (map (fn [x] (get mapping x)) signal))
        :else false))

(def SIGNAL_TO_NUM {#{\a \b \c \e \f \g} 0
                    #{\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})

(defn decodedArrToNum [arr]
  (reduce + (map-indexed (fn [idx sig] (* (Math/pow 10 (- (count arr) idx 1)) (SIGNAL_TO_NUM sig))) arr)))

; characters that haven't been mapped (ie the mapping doesnt specify what they actually should be)
(defn unmappedChars [mapping]
  (set/difference ALPHABET (keys mapping)))

; characters that haven't been used (ie there is no character in the mapping that results in them)
(defn unusedChars [mapping]
  (set/difference ALPHABET (vals mapping)))

(defn validResult [x]
  (contains? #{#{\a \b \c \e \f \g} #{\c \f} #{\a \c \d \e \g} #{\a \c \d \f \g} #{\b \c \d \f} #{\a \b \d \f \g} #{\a \b \d \e \f \g}#{\a \c \f} #{\a \b \c \d \e \f \g} #{\a \b \c \d \f \g}} x))

(defn tryWith [signals mapping knownValues]
  (cond
    (not (every? (fn [[in expected]]
                   (def attemptedDecode (decodeWith in mapping))
                   (or (= false attemptedDecode) (= expected attemptedDecode)))
                 knownValues)) false ; stop considering path if it breaks any known values
    (not (every? (fn [in]
                   (def attemptedDecode (decodeWith in mapping))
                   (or (= false attemptedDecode) (validResult attemptedDecode)))
                  signals)) false ; stop when any of the fully resolved signals are invalid
    (every? validResult (map (fn [x] (decodeWith x mapping)) signals)) mapping ; base case - all resolved to valid values
    :else (let [tryingToMap (first (unmappedChars mapping))]
            (first (filter (comp true? boolean) (for [candidate (unusedChars mapping)]
                                                  (do
                                                    (tryWith signals (assoc mapping tryingToMap candidate) knownValues))))))))

(def KNOWN_VALUES {2 #{\c \f} 4 #{\b \c \d \f} 3 #{\a \c \f} 7 #{\a \b \c \d \e \f \g}})

(defn extractKnownValues [signals]
  (reduce (fn [m s] (cond (contains? KNOWN_VALUES (count s)) (assoc m s (get KNOWN_VALUES (count s)))
                          :else m)) {} signals))

(def input (as-> (slurp "./input") x
             (str/split x #"\n")
             (map (fn [l]
                    (map (fn [p] (map set (str/split (str/trim p) #" "))) (str/split l #"\|"))) x)))

(def lineMappings (map (fn [l] (let [signals (set (flatten l))]
                                 (tryWith signals {} (extractKnownValues signals)))) input))
(def decodedOutputs (map (fn [l m] (let [signals (second l)]
                                     (map (fn [x] (decodeWith x m)) signals))) input lineMappings))
(def outputs (map decodedArrToNum decodedOutputs))

(println (reduce + outputs))