adventofcode 2020-12-06

Good morning !

๐Ÿ˜ 4

Hello! Todayโ€™s problem was pretty straightforward.. I am tied with the amount of problems I finished last year.. Not close to burning out.. I wonder if it is going easy on us this year?

I am awaiting the difficult problems like a surfer awaiting its wave.

You can pick one from the last year ^_^

๐Ÿ‘ 1
๐Ÿ’ฏ 1

I will keep them for January.

Agree! #6 usually starts ramping way up.

why give us a difficult problem on the weekend when you can save it for a weekdayโ€ฆ

๐Ÿ˜† 2

During the first 1 min 30, I was staring at a blank page, waiting for the page to load.

AoC really starts the first time you add

(set! *unchecked-math* :warn-on-boxed)
(set! *warn-on-reflection* true)
to a namespace

๐Ÿ‘Ÿ 1
๐Ÿƒโ€โ™‚๏ธ 1
๐Ÿ˜ 3

I like @dosโ€™s solution, which did not separate the input loading from the processing for the solution. It might be faster to type in that way.

This is the first year I'm (seriously) trying to do this. Does it usually get harder day by day?

(Today's puzzle was pretty easy, but if they're going to get difficult I might not have time to see it through.)

Problems have been surprisingly simple so far this year

Last year for me, was by far the most difficult of them all

Thatโ€™s probably because weโ€™re going to vacation ๐Ÿ˜ƒ

๐Ÿ˜„ 1

that or heโ€™s lulling us into complacency just before dropping the hammer tomorrow

oh you got work Monday morning? Hope you remember A* search

๐Ÿ˜ 4

I skipped AoC last year because the year before I ended up spending sometimes multiple hours on a single solution. If it gets like that again I'm out, but a bit more challenging than today's would be nice too ๐Ÿ™‚

Iโ€™ve done them since 2015, finished most but not all of them.. Except last year i bailed very early

Today's video: https://www.youtube.com/watch?v=b0a5siw85N4&feature=youtu.be I also talk a bit about the different ways that people solved yesterday's puzzle

๐Ÿ‘ 2

Many thanks to folks who dropped into the live chats or left comments!

Let me know if I leave too many comments during the streaming ๐Ÿ˜… I have a hard time holding them while excited by the contest.

All good, it's nice to see some activity :)

they definitely get difficult and I was spending hours on problems routinely, but iโ€™m not a fast solver

last year by like day 14 you had implemented a VM for a made up language, and then implemented pong using that language

I'm not a fast solver either, and also I'm doing them in Clojure, which I don't really know, so I'm extra slow.

i know the feelingโ€ฆ iโ€™m trying to go back and do them in Go after I finish in Clojure, and it is โ€ฆ painful (i donโ€™t know Go)

lots of typing

Day 6 Answers Thread - Post your answers here

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

(def input "...")

(def groups (str/split input #"\n\n"))

;; Part I
(apply + (for [group groups] (count (distinct (str/replace group #"\n" "")))))

;; Part II
(apply + (for [group groups
               :let [people (str/split-lines group)
                     answer-sets (map set people)]]
           (count (apply set/intersection answer-sets))))

(ns aoc.2020.d06
  (:require [aoc.file-util :as file-util]
            [clojure.set :refer [intersection]]
            [clojure.string :as str]))

(def input (map str/split-lines (file-util/read-chunks "2020/d06.txt")))

(defn part-1 [input]
  (reduce + (map (comp count distinct (partial apply str)) input)))

(defn part-2 [input]
  (reduce + (map (comp count (partial apply intersection) (partial map set)) input)))

first pass, feels inelegant

i feel the same way, but ๐Ÿคท

im guessing thereโ€™s a frequencies-based solution

I misread part1 and actually solve part2 first ๐Ÿ™‚ https://github.com/benfle/advent-of-code-2020/blob/main/day6.clj

@djblue nice ... (str/split input #"*\n\n*") that did not occur to me, I do a (partition-by empty?) ๐Ÿ™‚

๐Ÿ’ฏ 1
1

Noob code coming up - but I am actually quite happy with this code, it reads nicely I think:

(defn count-group [group]
  (->> group
       (string/split-lines)
       (map set)
       (apply clojure.set/intersection)
       (count)))

(->> (string/split (slurp "adv6.input.txt") #"\n\n")
     (map count-group)
     (reduce +))

I love the -> and ->> macros, but sometimes they can't be used because the place to put the previous form is different ... any tips for that particular problem? Is there a -?> which "does the right thing depending on the form ๐Ÿ˜„

Answering my self: I just searched and found as->, a very nice function. This allows me to write e.g.

(as-> "adv6.input.txt" v
      (slurp v)
      (string/split v #"\n\n")
      (map count-group v)
      (reduce + v))

@narve YMMV, but I try not to use as->, I find it has maintenance problems for me. I like to just pull out the different one as a let, so thereโ€™s only 1 threading type. In this case, could do:

(let [groups (-> path slurp (str/split #"\n\n")]
  (->> groups...))

yeah, I c your point. I can also use minor (-> or (->> lines within the larger thread-block as well. Just gotta pick the right tool for the job ๐Ÿ™‚

Day 7 Answers Thread - Post your answers here

@zelark is my hero. his solution in this thread blows me away.

โž• 2

I didnโ€™t finish Part II in this style yet, but I decided to try and do day7 in a rules engine (#clara). Thereโ€™s a better way to write this, but this gets the right answer:

(defrecord ContainerBag [type contained-bags])
(defrecord IntermediateBag [parent-type type])
(defrecord GoldBag [parent-type])

(defrule produce-intermediate-bags
  [ContainerBag
   (= ?type type)
   (= ?contained-bags contained-bags)]
  =>
  (when (seq ?contained-bags)
    (insert-all!
     (mapv (fn [[kind num]]
             (if (= kind "shiny gold")
               (map->GoldBag {:parent-type ?type})
               (map->IntermediateBag {:parent-type ?type
                                      :type kind})))
           ?contained-bags))))

(defrule indirect-gold-bags
  [ContainerBag
   (= ?type type)]
  [IntermediateBag
   (= ?type parent-type)
   (= ?intermediate-type type)]
  [GoldBag
   (= ?intermediate-type parent-type)]
  =>
  (insert! (map->GoldBag {:parent-type ?type})))

(defquery gold-bags []
  [?gold-bags <- (acc/distinct :parent-type) :from [GoldBag]])

(defn run-rules []
  (-> (mk-session :cache false)
      (insert-all (doall (for [[k vs] (parse input)]
                           (map->ContainerBag {:type k
                                               :contained-bags vs}))))
      (fire-rules)))
;; Part I
(-> (run-rules)
    (query gold-bags)
    first
    :?gold-bags
    count)
;; => 179

It would be cool to dynamically generate defrules for each line

I need to improve my regex capture skills. Let me know if you have a fast way to do the parsing/capturing, I am interested to learn.

not ideal, but I came up with this for parsing

(defn parse-entry [entry]
  (let [p  (re-find #"\w+ \w+" entry)
        cs (->> (re-seq #"(\d+) (\w+ \w+)" entry)
                (reduce (fn [m [_ v k]] (assoc m k (Long/parseLong v))) {}))]
    [p cs]))

(def bags (into {} (map parse-entry (str/split-lines input))))

I just realized that the algorithm for part2 could be used for part1

My naive solution: https://github.com/oxalorg/aoc/blob/main/src/puzzle7.clj Using some kind of memo could speed up first pass, but this works! ^_^

Mine:ย https://github.com/transducer/adventofcode/blob/master/src/adventofcode/2020/day7.clj I donโ€™t know why exactly I had to call set on the tree-seq entries in part 1 nor why I had to do apply + 1 in part 2, but it worksย ๐Ÿ™‚

โค๏ธ 2

I forgot about tree-seq โ€ฆ thatโ€™s a good idea.

๐Ÿ™‚ i now see set is because you have to find amount of different colors and + 1 is because you also have to count the containing bag

(into (set found-bags) (mapcat #(find-outer-bags bags %) found-bags)) nice @zelark

๐Ÿ˜ 1
โž• 1

Wow nice, I tried to use core.logic last year for a somewhat similar problem (https://adventofcode.com/2019/day/14) but I did not manage

๐Ÿ‘ 1

@erwinrooijakkers thanks to your solution for part 2, I simplified mine. Now itโ€™s pretty straightforward

(defn count-bags [bags [bag n]]
  (* n (apply + 1 (map #(count-bags bags %) (get bags bag)))))

๐Ÿ˜Š 2

(loop [bags #{"shiny gold"}
       old-bags #{}]
  (if (= bags old-bags)
    (dec (count bags))
    (let [new-bags (into bags (map first
                                   (filter #(seq (clojure.set/intersection bags (second %))) data)))]
      (recur new-bags bags))))

(defn get-bags-inside [times [color amount]]
  [(mapv (partial get-bags-inside (* times amount)) (data color)) (* times amount)])

(dec (apply + (flatten (get-bags-inside 1 ["shiny gold" 1]))))

Nothing very exciting in my solution but I will post anyway ๐Ÿ™‚ https://github.com/benfle/advent-of-code-2020/blob/main/day7.clj It took me way too long to figure out I was off by one for part 2 because I was counting the top shiny gold bag :)

What am I missing with this regex? Works fine for groups of 1 or two but fails with 3 or more (the second group gets improperly matched)

(\w+ \w+) bags? contain ((\d+) (\w+ \w+).*? bags?)(, (\d+) (\w+ \w+) bags?)*\.

the * doesnโ€™t repeat the capture group like you think it does

iโ€™d consider re-seq

> What am I missing with this regex? @jculp if you repeat a group, this group will be overwritten by the next match. For such cases you need to split a line, or just write 2 regex. Check my solution above for example.

heres mine, O'm definitely interested to see what others came up with today as I'm fairly new to clojure still https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/07.clj

Here's mine: https://github.com/rjray/advent-2020-clojure/blob/master/src/advent_of_code/day07.clj Writing the parsing took longer than I'd like, but I really got stuck in part 1 due to a small logic-error. Part 2 only took about 10 more minutes.

https://redpenguin101.github.io/posts/2020_12_07_aoc2020_day7.html. So impressed at how concise some of the answers here are. That was a fun problem though!

โž• 1

Again, lots of effort spent on processing the raw input into a nice shape. I notice my part 2 recursive function has the same pattern as several others. Took me some grinding to get it working. Still not pretty, oh well.

;; with input processed as:
;;     ["dull aqua" ([4 "dark fuchsia"] [1 "shiny purple"])]
(defn count-nestedbags [inp topbag]
  (letfn [(rcount [bag]
            (let [[_ bt] bag
                  contained (second (first (filter #(= bt (first %)) inp)))]
              (apply + 1
                     (for [[bc bn :as thebag] contained]
                       (if (= bn "no other") bc
                         (* bc (rcount thebag)))))))]
    (dec (rcount [1 topbag]))))

(count-nestedbags input-part2 "shiny gold")

This was so hard for me. It took me a lot of time to realize my first regex approach would not work. Then I had struggles wrapping my head around the recursion I needed. Then I made all sorts of silly mistakes even though the structure was rightโ€ฆ And the result aint pretty!

(comment ; step 1
  (def input (util/fetch-input 7))
  (->> (clojure.string/split-lines input)
       (map #(clojure.string/split % #"[ ,]"))
       (map #(->> %
                  (partition 2 5)
                  (map vec)
                  (map (partial apply str))))
       ((fn dig [look-for found rules]
          (let [directs (reduce (fn [acc [bag & bags]]
                                  (if (seq (clojure.set/intersection (set bags) look-for))
                                    (conj acc bag)
                                    acc))
                                #{}
                                rules)]
            (if (seq directs)
              (dig directs (conj found directs) rules)
              found)))
        #{"shinygold"} #{})
       (apply clojure.set/union)
       (count)))

(comment ; step 2
  (def input (util/fetch-input 7))
  (->> (clojure.string/split-lines input)
       (map #(clojure.string/split (str "1 " %) #"[ ,]"))
       (remove #((set %) "no"))
       (map #(->> %
                  (partition 3 5)
                  (map (fn [[n c1 c2]]
                         [(Integer/parseInt n) (str c1 c2)]))))
       ((fn dig [look-for found rules]
          (let [directs (reduce (fn [acc [[_n bag-color] & bags]]
                                  (->> (for [color look-for
                                             :when (= color bag-color)]
                                         (map #(repeat (first %) %) bags))
                                       (apply concat)
                                       (apply concat)
                                       (into acc)))
                                []
                                rules)]
            (if (seq directs)
              (dig (map second directs) (into found directs) rules)
              found)))
        ["shinygold"] [])
       (count)))

when I have troubles naming things, I know I donโ€™t know what I am doing, but I pressed on on sheer intuition. ยฏ\(ใƒ„)/ยฏ

Day 8 Answers Thread - Post your answers here

A reason part 2 took me so long is that I was looking at it as a tree problem. So I was able to build a structure something like so (simplyfied):

[[nop jmp]
 [acc]
 [acc]
 [jmp nop]
 [acc]]
But then I failed building the programs from this. Which for the example above would be:
[[nop acc acc jmp acc]
 [jmp acc acc jmp acc]
 [nop acc acc nop acc]
 [jmp acc acc nop acc]]
Does anyone have any pointers on how to go about it?

@pez Here is what I would do to optimize this problem: โ€ข Collect all the switch-indexes , indexes of the nop and jmp where the original program go before an infinite loop is detected, because the solution is to flip one of those indexes. โ€ข Build the reverse-jmp-graph, where edges are from jmp-targets to jmp-sources. โ€ข In this graph, collect all land-indexes which are reachable from the end of the program. โ€ข Find which change on instructions at switch-indexes let the program go into instructions at land-indexes .

And my final solution: https://github.com/zelark/AoC-2020/blob/master/src/zelark/aoc_2020/day_08.clj in the Part 2 firstly I used case , but re-implement it with a hash-map as @plexus showed on the live stream.

I always feel filthy when I use atomsโ€ฆ. This one is pretty dirty, but I got it done: https://github.com/jreighley/aoc2020/blob/master/src/day8.clj

definitely a bit engineered, but Iโ€™m not getting caught off-guard by a part 2: intcode boogaloo https://github.com/motform/advent-of-clojure/blob/master/src/advent-of-clojure/2020/eight.clj

Day 8 makes me appreciate immutable data structures and defmulti. I am fairly happy with my code for a change. https://github.com/chamaeleon/aoc2020-clj/blob/master/src/aoc2020/day08.clj

https://redpenguin101.github.io/posts/2020_12_08_aoc2020_day8.html - this one did give me some flashbacks to intcode ๐Ÿ˜จ

๐Ÿ‘ 1

I really liked the intcode ones last year so I hope there's some future days that build this one up Anyways my solution for day 08 https://github.com/listba/advent-of-code-2020/blob/master/clojure/src/aoc_2020/days/08.clj

I tried for so long do step 2 in a tree-ish way. Gave up. The brute force was with me though. Copying the program and modifying each one differently worked. I want my gold stars! Glad I learned about cond-> the other day.

(defn parse [input]
  (->> (clojure.string/split-lines input)
       (map #(clojure.string/split % #" "))
       (mapv (fn [[op arg]] [op (Integer/parseInt arg)]))))

(defn run [program]
  (let [length (count program)]
    (reduce (fn [cpu _]
              (let [{:keys [pc pcs]} cpu]
                (if (< pc length)
                  (let [[op arg]         (nth program pc)]
                    (if (pcs pc)
                      (reduced cpu)
                      (cond-> cpu
                        (= op "acc")        (update :acc #(+ % arg))
                        (#{"acc" "nop"} op) (update :pc inc)
                        (= op "jmp")        (update :pc #(+ % arg))
                        :always             (update :pcs conj pc))))
                  (reduced cpu))))
            {:acc    0
             :pc     0
             :length (count program)
             :pcs    #{}}
            (range))))

(comment
  (def input (util/fetch-input 8))
  ; step 1
  (->> input
       (parse)
       (run)
       :acc)

  ; step 2
  (->> input
       (parse)
       (#(repeat (count %) %))
       (keep-indexed (fn [i program]
                       (let [[op val] (program i)]
                         (assoc program i [(cond
                                             (= "nop" op) "jmp"
                                             (= "jmp" op) "nop"
                                             :else        op)
                                           val]))))
       (map run)
       (filter #(= (:pc %) (:length %)))
       (first)
       :acc))

๐ŸŽ‰ 1

feels like part 2 could be way nice even brute forcing itโ€ฆ but I dunno how

It reminds me my old emulator https://github.com/green-coder/girlfriend-advance

๐Ÿฆœ 1
๐ŸŽฎ 5

(defn run-code [code]
  (let [history (atom #{})]
    (loop [acc 0
           ip  0]
      (let [[op arg] (nth code ip [:hlt 0])]
        (if (contains? @history ip)
          {:inf-loop acc}
          (do (swap! history conj ip)
              (case op
                :hlt {:halt acc}
                :nop (recur acc (inc ip))
                :acc (recur (+ acc arg) (inc ip))
                :jmp (recur acc (+ ip arg)))))))))

@maksut.cagil has joined the channel

I've learnt a few neat functions from looking at you guys solution so far this year ๐Ÿ™‚

๐Ÿ’ฏ 1

In case you havenโ€™t seen it, I highly recommend being well versed in all these base functions, really helps to know whatโ€™s possible. https://clojure.org/api/cheatsheet

๐Ÿ‘ 1

@lukas.block has joined the channel

I am doing 2015 in parallel ๐Ÿ˜•

split keyboard? ๐Ÿ˜‰