Fork me on GitHub
#adventofcode
<
2020-12-14
>
Vincent Cantin04:12:04

Day 14 answers thread - Post your answers here

Daw-Ran Liou07:12:07

TIL bit-set and bit-clear 😄

👏 9
bellissimo 3
alekszelark07:12:45

@U7PQH43PS a bit refactored your floating function 😊

(defn floating-addresses [mask address]
  (let [parsed-mask (map vector (iterate dec 35) mask)
        inner (fn inner [mask address]
                (if-let [[n m] (first mask)]
                  (case m
                    \0 (inner (next mask) address)
                    \1 (inner (next mask) (bit-set address n))
                    \X (concat (inner (next mask) (bit-clear address n))
                               (inner (next mask) (bit-set address n))))
                  [address]))]
    (inner parsed-mask address)))

🙌 6
❤️ 3
🎉 3
parrot 3
erwinrooijakkers07:12:22

Nice @U067R559Q and @U076FM90B. I didn’t know about bit-set and bit-test 🙂. Maybe I will clean mine up this evening. Dirty solution using bit-and and bit-or: https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day14.clj

👍 3
erwinrooijakkers07:12:20

Also my use of tree recursion and then calling flatten to get the leave values I don’t like, but not sure how to improve

erwinrooijakkers07:12:02

Maybe by using similar pattern as for floating-addresses above with concat

alekszelark08:12:33

I like floating-addresses approach

alekszelark08:12:20

gonna refact my solution after work

6
pez12:12:24

My step 1:

(defn parse [input]
  (->> (clojure.string/split-lines input)
       (map #(->> (clojure.string/split % #" ")
                     ((fn [[op _ arg]] [op arg]))))
       (map (fn [[op arg]]
              (if (= op "mask")
                {:mask arg}
                {:addr op :value (Long/parseLong arg)})))))

(comment
  (def input (util/fetch-input 14))
  ; step 1
  (def input "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0")
  (->> input
       (parse)
       (reduce (fn [acc instruction]
                 (if-let [mask (:mask instruction)]
                   (merge acc
                          {:mask mask
                           :mask-or (->> (clojure.string/replace mask #"X" "0")
                                         (str "2r")
                                         (read-string))
                           :mask-and (->> (clojure.string/replace mask #"X" "1")
                                          (str "2r")
                                          (read-string))})
                   (let [{:keys [addr value]} instruction
                         {:keys [mask-and mask-or]} acc
                         masked-value (-> value
                                          (bit-or mask-or)
                                          (bit-and mask-and))]
                     (-> acc
                         (update-in [:memory addr] #(bit-or mask-or masked-value (or % 0)))
                         (update-in [:memory addr] #(bit-and mask-and masked-value (or % 0)))))))
               {})
       :memory
       (vals)
       (apply +)))

👍 6
benoit13:12:31

My solution to Day 14. I couldn't find a nice way to represent the second part. https://github.com/benfle/advent-of-code-2020/blob/main/day14.clj

benoit13:12:17

My worst performance of the season (according to my criterias) 🙂

alekszelark14:12:56

I’m glad someone uses str/escape now 🙂

Vincent Cantin14:12:20

That’s thanks to @U07FP7QJ0 and his videos

Vincent Cantin14:12:38

He saw it somewhere and it spreads

👍 6
Vincent Cantin14:12:46

was it from you?

alekszelark14:12:00

Yes 🙂

🍻 12
😊 3
👍 6
alekszelark14:12:55

Also cleaned up my code. Thanks @genmeblog for inspiration https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_14.clj

💯 3
Joe14:12:02

https://github.com/RedPenguin101/aoc2020/blob/main/day14.clj - part 2 is possibly the ugliest code ever written, aside from taking over a second to run. Tidy up time!

alekszelark16:12:15

@genmeblog, btw in the part 2 just bit-or is enough

👍 3
genmeblog16:12:10

Oh, I will revisit it later, thanks! Very clean solution btw (yours)!

alekszelark19:12:09

I ran my code with a difficult input from this thread, waited ~4 minutes and then interrupted it. Anyone else wants to test it? https://www.reddit.com/r/adventofcode/comments/kcybyr/2002_day_14_part_2_but_what_if_the_input_is_harder/

Vincent Cantin19:12:50

I thought about it, but ran for the simplest solution.

pez21:12:18

I eventually did this differently, but a while I was trying this way:

masked-addr (-> addr
                (#(Integer/toBinaryString %))
                (->> (format "%36s"))
                (clojure.string/escape {\space \0})
                (->> (map (fn masker [m a]
                            (case m
                              \0 [a]
                              \1 [\1]
                              \X [\1 \0]))
                          mask)))
The relevant part there is how I encoded the masked address. It produces the following for the address 26 assignment in the example input:
"000000000000000000000000000000X1001X"
 ([\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\0]
  [\1 \0]
  [\1]
  [\1]
  [\0]
  [\1]
  [\1 \0])
I was pleased getting that far and thought the next step would be easy. I think about it as finding all different paths from start to end. But when I try to produce the addresses from them in a horribly nested manner. Like so:
[[[[59 100] [58 100]] [[27 100] [26 100]]] [[[[27 1] [26 1]] [[25 1] [24 1]]] [[[19 1] [18 1]] [[17 1] [16 1]]]]]
And I can’t figure out how to not produce it that way. This is my floater reducer:
(reduce (fn [acc [masked-addr value]]
                 (let [floater (fn floater [addr floating]
                                 (if-not (seq? addr)
                                   [(->> floating 
                                         (apply (partial str "2r"))
                                         (read-string)) 
                                    value]
                                   (let [d (first addr)]
                                     (if (= 1 (count d))
                                       (floater (next addr) (conj floating (first d)))
                                       [(floater (next addr) (conj floating (first d)))
                                        (floater (next addr) (conj floating (second d)))]))))]
                   (conj acc (floater masked-addr []))))
               [])
… help?

pez21:12:08

(I also tried using for but that got even uglier. 😃

pez22:12:01

I figured it out now. At least I figured out a way to get this neat structure:

[[(59 58 27 26) 100] [(27 26 25 24 19 18 17 16) 1]]
concat is my buddy

pez22:12:09

Next question then. This is my working floater:

(fn floater [addr floating]
  (if-not (seq? addr)
    [(->> floating
          (apply (partial str "2r"))
          (read-string))]
    (let [ds (first addr)]
      (if (= 1 (count ds))
        (floater (next addr) (conj floating (first ds)))
        (mapcat #(floater (next addr) (conj floating %)) ds)))))
It seems to me that this should be a general problem, finding all paths through a structure like this. I think I can generalize my solution, but wonder if there might already be one out there? In core even? My google fu fails me.

pez23:12:19

So, then I am ready to share my solution. It takes half a sec to step 2 compute my input, so clearly there are cpu cycles wasted, but I learnt a lot fiddling with this to and from today. Please feel invited to provide feedback to this Clojure learner:

pez23:12:19

(defn parse [input]
  (->> (clojure.string/split-lines input)
       (map #(->> (clojure.string/split % #" ")
                  ((fn [[op _ arg]] [op arg]))))
       (map (fn [[op arg]]
              (if (= op "mask")
                {:mask arg}
                {:addr (->> (clojure.string/replace op #"^\D+|\D+$" "")
                            (Long/parseLong))
                 :value (Long/parseLong arg)})))))

(comment
  (def input (util/fetch-input 14))
  (def input "mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0")
  ; step 1
  (->> input
       (parse)
       (reduce (fn [acc instruction]
                 (if-let [mask (:mask instruction)]
                   (merge acc
                          {:mask mask
                           :mask-or (->> (clojure.string/replace mask #"X" "0")
                                         (str "2r")
                                         (read-string))
                           :mask-and (->> (clojure.string/replace mask #"X" "1")
                                          (str "2r")
                                          (read-string))})
                   (let [{:keys [addr value]} instruction
                         {:keys [mask-and mask-or]} acc
                         masked-value (-> value
                                          (bit-or mask-or)
                                          (bit-and mask-and))]
                     (-> acc
                         (update-in [:memory addr] #(bit-or mask-or masked-value (or % 0)))
                         (update-in [:memory addr] #(bit-and mask-and masked-value (or % 0)))))))
               {})
       :memory
       (vals)
       (apply +))

  ; step 2
  (def input "mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1")
  (->> input
       (parse)
       (reduce (fn [acc instruction]
                 (def acc acc)
                 (if-let [mask (:mask instruction)]
                   (merge acc
                          {:mask mask})
                   (let [{:keys [addr value]} instruction
                         {:keys [mask]} acc
                         masked-addr (-> addr
                                         (#(Integer/toBinaryString %))
                                         (->> (format "%36s"))
                                         (clojure.string/escape {\space \0})
                                         (->> (map (fn masker [m a]
                                                     (case m
                                                       \0 [a]
                                                       \1 [\1]
                                                       \X [\1 \0]))
                                                   mask)))]
                     (update acc :assignments conj [masked-addr value]))))
               {:assignments []})
       :assignments
       (reduce (fn [acc [masked-addr value]]
                 (let [floater (fn floater [addr floating]
                                 (if-not (seq? addr)
                                   [(->> floating
                                         (apply (partial str "2r"))
                                         (read-string))]
                                   (mapcat #(floater (next addr) (conj floating %)) (first addr))))]
                   (conj acc [(floater masked-addr []) value])))
               [])
       (reduce (fn [assignments assignment]
                 (merge assignments
                        (let [[addresses value] assignment]
                          (reduce (fn [acc address]
                                    (assoc acc address value))
                                  {}
                                  addresses))))
               {})
       (vals)
       (apply +)))

🎉 3
curlyfry00:12:11

Most of the solution is generating a list of operations per bit index, so "1XX0" would be [0 bit-clear 3 bit-set] that are then applied to the value with reduce. For part 2 I use clojure.math.combinatorics/subsets to get all the ways of picking the floating indices and assign bit-set and bit-clear based on which indices are in each subset. A little weird but it works 🙂 https://github.com/Dexterminator/advent-of-code-2020/blob/main/src/day14/core.clj

👍 9
Vincent Cantin06:12:12

Good morning !

😁 12
Average-user13:12:58

Just when I decided to use Lua the problem requires 64-bit intgeres :c

opieop 3
😎 3